Обходной путь из-за отсутствия оператора nameof в C # для безопасной привязки данных? - PullRequest
43 голосов
/ 19 ноября 2008

Было много настроений включить оператор nameof в C #. В качестве примера того, как этот оператор будет работать, nameof(Customer.Name) вернет строку "Name".

У меня есть доменный объект. И я должен связать это. И тогда мне нужны имена свойств в виде строк. И я хочу, чтобы они были безопасны от типов.

Я помню, как наткнулся на обходной путь в .NET 3.5, который обеспечивал функциональность nameof и включал лямбда-выражения. Однако я не смог найти этот обходной путь. Кто-нибудь может предоставить мне этот обходной путь?

Мне также интересен способ реализации функциональности nameof в .NET 2.0, если это возможно.

Ответы [ 8 ]

77 голосов
/ 19 ноября 2008

Этот код в основном делает это:

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;
    }
}

(Конечно, это 3,5 кода ...)

6 голосов
/ 19 ноября 2008

Хотя reshefm и Jon Skeet показывают правильный способ сделать это с помощью выражений, стоит отметить, что есть более дешевый способ сделать это для имен методов:

Оберните делегата вокруг вашего метода, получите MethodInfo, и все готово. Вот пример:

private void FuncPoo()
{
}

...

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

К сожалению, это работает только для методов; это не работает для свойств, так как вы не можете иметь делегатов для методов получения или установки свойств. (Похоже, глупое ограничение, ИМО.)

4 голосов
/ 28 сентября 2014

Если кто-то не передумает, оператор nameof выглядит так, как будто он входит в C # 6. Вот примечания к совещанию по проектированию:

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

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

4 голосов
/ 11 января 2010

Расширение того, что сделал reshefm, которое упростило использование оператора nameof (), а также дает имена методов, а также членов класса и методов:

/// <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");
    }
}

Чтобы использовать это:

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 голосов
/ 19 ноября 2008

Обходной путь - использовать дерево выражений и разбить его на части, чтобы найти соответствующий MemberInfo. В этой заметке есть немного больше подробностей и комментариев (хотя это не код для извлечения члена - я думаю, что это где-то в другом вопросе).

К сожалению, поскольку деревья выражений в .NET 2.0 не существуют, в действительности нет эквивалента.

Одним из решений, позволяющих избежать опечаток, является наличие набора методов доступа, которые выбирают соответствующие PropertyInfo для определенного свойства и проводят их модульное тестирование. Это будет единственное место, где есть строка. Это позволит избежать дублирования и упростить рефакторинг, но он немного драконовский.

2 голосов
/ 19 ноября 2015

Принятое решение красиво, просто и элегантно.

Однако создание дерева выражений стоит дорого, и мне нужен полный путь свойства.

Так что я немного изменил это. Это совсем не элегантно, но просто и хорошо работает в большинстве случаев:

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;
}

Пример:

? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"
1 голос
/ 19 августа 2015

Это часть языка в C # 6.0

https://msdn.microsoft.com/en-us/magazine/dn802602.aspx

1 голос
/ 18 февраля 2013

Ответ от reshefm довольно хороший, но это немного проще API IMO:

Пример использования: 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;
    }
  }
}

Полный код здесь: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

...