繁体   English   中英

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

[英]Workaround for lack of 'nameof' operator in C# for type-safe databinding?

在 C# 中包含nameof运算符的观点很多。 作为此运算符如何工作的示例, nameof(Customer.Name)将返回字符串"Name"

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

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

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

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

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码了...)

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

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

private void FuncPoo()
{
}

...

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

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

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

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

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

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

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

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

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

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

但是,构建表达式树的成本很高,而且我需要整个属性路径。

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

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"

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM