2008-11-19 1 views
43

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

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

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

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

+0

смотри также http://stackoverflow.com/questions/1329138/how-to -make-databinding-type-safe-and-support-refactoring –

+2

Эта проблема теперь решена во время компиляции! Оператор ['nameof'] (https://msdn.microsoft.com/en-us/magazine/dn802602.aspx) был реализован в C# 6.0 с .NET 4.6 и VS2015 в июле 2015 года. Следующие ответы по-прежнему действительны для C# <6.0. – Mike

ответ

77

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

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-код ​​...)

+14

Обратите внимание, что существует штраф за исполнение. Объекты выражения довольно дороги для создания. Вызов 'Foo (Expression >)' в 200 раз медленнее, чем старомодный 'Foo (string propName)'. Пожалуйста, проголосуйте за [время компиляции имени оператора] (http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/2427047-add-nameof-operator-in-c-). –

+0

Обратите внимание, что этот код должен быть скомпилирован в VS2015, чтобы использовать Expression.Body – BillW

4

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

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

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

+0

Это то, что вы ищете? http://imaginarydevelopment.blogspot.com/2009/10/compile-time-safe-databinding.html ссылается на это http://stackoverflow.com/questions/1329138/how-to-make-databinding-type-safe- and-support-refactoring – Maslow

6

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

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

private void FuncPoo() 
{ 
} 

... 

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

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

+0

Я согласен. Глупое ограничение. Я не думаю, что для компилятора должно быть слишком сложно понять, является ли это получателем или сеттером, и пусть u использует его делегировать. – reshefm

4

УДЛИНИТЕЛЯ к тому, что 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. 
    } 
} 
1

ответ от 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

4

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

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

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

+0

Я так долго ждал этого. Настолько приятно видеть, что это выполняется. –

+0

И вот официальная документация для оператора ['nameof'] (https://msdn.microsoft.com/en-us/library/dn986596.aspx), который действительно стал доступен в C# 6.0 (и Visual Studio 2015) , – DavidRR

2

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

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

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

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" 
+0

Я создал общий метод: общественности статической строки PropertyPath (это T OBJ, Expression > выражение) { вар s = expression.Body.ToString(); var p = s.Remove (0, s.IndexOf ('.') + 1); return p; } –

+1

Из документации для оператора ['nameof'] (https://msdn.microsoft.com/en-us/library/dn986596.aspx) (нового для C# 6.0) в разделе ** Примечания ** : * Если вам нужно получить полное имя, вы можете использовать выражение * *** typeof *** * вместе с * *** nameof *** *. * – DavidRR