Obejście braku operatora "nameof" w C# dla bezpiecznego databindingu typu?

Było wiele sentymentów do włączenia nameof operatora w C#. Jako przykład działania tego operatora, nameof(Customer.Name) zwróci łańcuch "Name".

Mam obiekt domeny. I muszę to związać. I potrzebuję nazw właściwości jako ciągów. I chcę, żeby były bezpieczne.

Pamiętam, że natknąłem się na obejście w.NET 3.5, które zapewniało funkcjonalność nameof i obejmowało wyrażenia lambda. Nie udało mi się jednak zlokalizować tego obejście. Czy ktoś może mi to obejść?

Jestem również zainteresowany sposobem zaimplementowania funkcjonalności nameof w.NET 2.0, jeśli jest to możliwe.

Author: DavidRR, 2008-11-19

8 answers

Ten kod zasadniczo tak robi:

class Program
{
    static void Main()
    {
        var propName = Nameof<SampleClass>.Property(e => e.Name);

        Console.WriteLine(propName);
    }
}

public class Nameof<T>
{
    public static string Property<TProp>(Expression<Func<T, TProp>> expression)
    {
        var body = expression.Body as MemberExpression;
        if(body == null)
            throw new ArgumentException("'expression' should be a member expression");
        return body.Member.Name;
    }
}

(Oczywiście jest to kod 3.5...)

 77
Author: reshefm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 14:48:07

Podczas gdy Reshefm i Jon Skeet pokazują właściwy sposób, aby to zrobić za pomocą wyrażeń, warto zauważyć, że istnieje tańszy sposób, aby to zrobić dla nazw metod:

Owiń delegata wokół swojej metody, Pobierz MethodInfo i możesz iść. Oto przykład:

private void FuncPoo()
{
}

...

// Get the name of the function
string funcName = new Action(FuncPoo).Method.Name;

Niestety, działa to tylko dla metod; nie działa dla właściwości, ponieważ nie można mieć delegatów do metod getter lub setter właściwości. (Wydaje się głupie ograniczenie, IMO.)

 6
Author: Judah Gabriel Himango,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 15:18:57

Obejściem jest użycie drzewa wyrażeń i rozdzielenie tego drzewa wyrażeń w celu znalezienia odpowiedniego MemberInfo. Trochę więcej szczegółów i komentarzy jest w tej notce (chociaż nie kod do wyciągnięcia członka - to chyba w innym so question gdzieś).

Niestety, ponieważ drzewa wyrażeń nie istnieją w. NET 2.0, tak naprawdę nie ma ich odpowiedników.

Jednym z rozwiązań, aby uniknąć literówek, jest posiadanie zestawu accesorów, które pobierają odpowiednie PropertyInfo dla szczególną właściwość, a jednostka je przetestować. To byłoby jedyne miejsce, w którym był sznurek. Uniknęłoby to powielania i ułatwiłoby refaktoryzację, ale jest to trochę drakońskie.

 4
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-19 12:57:24

Rozszerzenie do tego, co zrobił reshefm, które uprościło użycie operatora nameof () i podaje nazwy metod oraz członków klasy i metod:

/// <summary>
/// Provides the <see cref="nameof"/> extension method that works as a workarounds for a nameof() operator, 
/// which should be added to C# sometime in the future.
/// </summary>
public static class NameOfHelper
{
    /// <summary>
    /// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression.
    /// </summary>
    /// <typeparam name="T">The type of the <paramref name="obj"/> parameter.</typeparam>
    /// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam>
    /// <param name="obj">An object, that has the property (or method), which its name is returned.</param>
    /// <param name="expression">A Lambda expression of this pattern: x => x.Property <BR/>
    /// Where the x is the <paramref name="obj"/> and the Property is the property symbol of x.<BR/>
    /// (For a method, use: x => x.Method()</param>
    /// <returns>A string that has the name of the given property (or method).</returns>
    public static string nameof<T, TProp>(this T obj, Expression<Func<T, TProp>> expression)
    {
        MemberExpression memberExp = expression.Body as MemberExpression;
        if (memberExp != null)
            return memberExp.Member.Name;

        MethodCallExpression methodExp = expression.Body as MethodCallExpression;
        if (methodExp != null)
            return methodExp.Method.Name;

        throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression");
    }

    /// <summary>
    /// Returns a string represantaion of a property name (or a method name), which is given using a lambda expression.
    /// </summary>
    /// <typeparam name="TProp">The type of the property (or the method's return type), which is used in the <paramref name="expression"/> parameter.</typeparam>
    /// <param name="expression">A Lambda expression of this pattern: () => x.Property <BR/>
    /// Where Property is the property symbol of x.<BR/>
    /// (For a method, use: () => x.Method()</param>
    /// <returns>A string that has the name of the given property (or method).</returns>
    public static string nameof<TProp>(Expression<Func<TProp>> expression)
    {
        MemberExpression memberExp = expression.Body as MemberExpression;
        if (memberExp != null)
            return memberExp.Member.Name;

        MethodCallExpression methodExp = expression.Body as MethodCallExpression;
        if (methodExp != null)
            return methodExp.Method.Name;

        throw new ArgumentException("'expression' should be a member expression or a method call expression.", "expression");
    }
}

Aby go użyć:

static class Program
{
    static void Main()
    {
        string strObj = null;
        Console.WriteLine(strObj.nameof(x => x.Length)); //gets the name of an object's property.
        Console.WriteLine(strObj.nameof(x => x.GetType())); //gets the name of an object's method.
        Console.WriteLine(NameOfHelper.nameof(() => string.Empty)); //gets the name of a class' property.
        Console.WriteLine(NameOfHelper.nameof(() => string.Copy(""))); //gets the name of a class' method.
    }
}
 4
Author: Ad P.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-11 12:00:44

Chyba, że ktoś zmieni zdanie, operator nameof wygląda na to, że wchodzi w C # 6. Oto uwagi na temat spotkania projektowego:

Https://roslyn.codeplex.com/discussions/552376

Https://roslyn.codeplex.com/discussions/552377

 4
Author: Ronnie Overby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-28 13:56:13
 2
Author: kotpal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-18 21:47:29

Przyjęte rozwiązanie jest ładne, proste i eleganckie.

Jednak Budowanie drzewa wyrażeń jest kosztowne i potrzebuję całej ścieżki własności.

Więc trochę go zmieniłem. Nie jest wcale elegancki, ale jest prosty i działa dobrze w większości przypadków: {]}
public static string Property<TProp>(Expression<Func<T, TProp>> expression)
{
    var s = expression.Body.ToString();
    var p = s.Remove(0, s.IndexOf('.') + 1);
    return p;
}

Przykład:

? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"
 2
Author: Larry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-19 08:29:02

Odpowiedź z reshefm jest całkiem dobra, ale to jest trochę prostsze API IMO:

Przykład użycia: NameOf.Property(() => new Order().Status)

using System;
using System.Diagnostics.Contracts;
using System.Linq.Expressions;

namespace AgileDesign.Utilities
{
public static class NameOf
{
    ///<summary>
    ///  Returns name of any method expression with any number of parameters either void or with a return value
    ///</summary>
    ///<param name = "expression">
    ///  Any method expression with any number of parameters either void or with a return value
    ///</param>
    ///<returns>
    ///  Name of any method with any number of parameters either void or with a return value
    ///</returns>
    [Pure]
    public static string Method(Expression<Action> expression)
    {
        Contract.Requires<ArgumentNullException>(expression != null);

        return ( (MethodCallExpression)expression.Body ).Method.Name;
    }

    ///<summary>
    ///  Returns name of property, field or parameter expression (of anything but method)
    ///</summary>
    ///<param name = "expression">
    ///  Property, field or parameter expression
    ///</param>
    ///<returns>
    ///  Name of property, field, parameter
    ///</returns>
    [Pure]
    public static string Member(Expression<Func<object>> expression)
    {
        Contract.Requires<ArgumentNullException>(expression != null);

        if(expression.Body is UnaryExpression)
        {
            return ((MemberExpression)((UnaryExpression)expression.Body).Operand).Member.Name;
        }
        return ((MemberExpression)expression.Body).Member.Name;
    }
  }
}

Pełny kod jest tutaj: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

 1
Author: Sergey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-22 14:37:12