C# 中缺少用于类型安全数据绑定的“nameof”运算符的解决方法?

Posted

技术标签:

【中文标题】C# 中缺少用于类型安全数据绑定的“nameof”运算符的解决方法?【英文标题】:Workaround for lack of 'nameof' operator in C# for type-safe databinding? 【发布时间】:2010-09-23 01:00:34 【问题描述】:

在 C# 中包含 nameof 运算符引起了很多人的注意。作为该运算符如何工作的示例,nameof(Customer.Name) 将返回字符串 "Name"

我有一个域对象。我必须绑定它。然后我需要属性名称作为字符串。我希望它们是类型安全的。

我记得在 .NET 3.5 中遇到过一种解决方法,它提供了 nameof 的功能并涉及 lambda 表达式。但是,我无法找到此解决方法。任何人都可以向我提供这种解决方法吗?

如果可能的话,我还对在 .NET 2.0 中实现 nameof 功能的方法感兴趣。

【问题讨论】:

另见***.com/questions/1329138/… 这个问题现在在编译时解决了! nameof 运算符于 2015 年 7 月在 C# 6.0 和 .NET 4.6 和 VS2015 中实现。以下答案对于 C# 【参考方案1】:

这段代码基本上是这样做的:

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代码……)

【讨论】:

请注意,存在性能损失。创建表达式对象的成本非常高。调用Foo(Expression&lt;Func&lt;&gt;&gt;) 比老式的Foo(string propName) 慢200 倍。请投票给compile-time nameof operator。 注意这段代码必须在VS2015中编译过才能使用Expression.Body【参考方案2】:

虽然 reshefm 和 Jon Skeet 展示了使用表达式执行此操作的正确方法,但值得注意的是,对于方法名称还有一种更便宜的方法:

在您的方法周围包裹一个委托,获取 MethodInfo,然后您就可以开始了。这是一个例子:

private void FuncPoo()



...

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

不幸的是,这仅适用于方法;它不适用于属性,因为您不能拥有属性 getter 或 setter 方法的委托。 (IMO,这似乎是一个愚蠢的限制。)

【讨论】:

我同意。愚蠢的限制。我不认为编译器应该很难理解它是 getter 还是 setter,让你使用委托它。【参考方案3】:

解决方法是使用表达式树,然后将该表达式树拆开以找到相关的MemberInfo。 this note 中有更多细节和评论(尽管不是提取成员的代码 - 我相信这是另一个 SO 问题)。

很遗憾,由于 .NET 2.0 中不存在表达式树,因此实际上没有等效项。

避免拼写错误的一个解决方案是使用一组访问器来获取特定属性的相关PropertyInfo,并对它们进行单元测试。那将是唯一有字符串的地方。这将避免重复并使重构更容易,但它有点苛刻。

【讨论】:

这是您要找的吗? imaginarydevelopment.blogspot.com/2009/10/… 它引用了这个 ***.com/questions/1329138/…【参考方案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.
    

【讨论】:

【参考方案5】:

除非有人改变主意,否则nameof 运算符看起来像是在 C# 6 中出现。以下是关于它的设计会议记录:

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

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

【讨论】:

我已经等了这么久了。很高兴看到它正在实施。 这里是 nameof 运算符的官方文档,它确实在 C# 6.0(和 Visual Studio 2015)中可用。【参考方案6】:

这是 C# 6.0 语言的一部分

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

【讨论】:

【参考方案7】:

公认的解决方案很好,简单而优雅。

但是,构建表达式树很昂贵,我需要整个属性路径。

所以我改变了一点。它一点也不优雅,但它很简单,在大多数情况下都能很好地工作:

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"

【讨论】:

我创建了通用方法: public static string PropertyPath(this T obj, Expression> expression) var s = expression.Body.ToString() ; var p = s.Remove(0, s.IndexOf('.') + 1);返回 p; 来自nameof 运算符的文档(C# 6.0 的新功能),在 Remarks 部分:如果您需要获得完全限定的名称,您可以使用 typeof 表达式与 nameof .【参考方案8】:

reshefm 的回答还不错,不过这个 API IMO 稍微简单一点:

使用示例: NameOf.Property(() =&gt; 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

【讨论】:

以上是关于C# 中缺少用于类型安全数据绑定的“nameof”运算符的解决方法?的主要内容,如果未能解决你的问题,请参考以下文章

c# nameof

c# nameof

C# 11 更加实用的 nameof

Qt 的 moc/C++11 是不是与 C# 的 nameof() 运算符等效?

用于数据绑定的 IntelliSense 不起作用

优化C#程序的四十七种方法