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

时间:2023-01-07 11:46:13

There has been a lot of sentiment to include a nameof operator in C#. As an example of how this operator would work, nameof(Customer.Name) would return the string "Name".

在C#中包含一个名称操作符已经有很多情绪。作为此运算符如何工作的示例,nameof(Customer.Name)将返回字符串“Name”。

I have a domain object. And I have to bind it. And I need names of properties as strings then. And I want them to be type-safe.

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

I remember coming across a workaround in .NET 3.5 which provided the functionality of nameof and involved lambda expressions. However, I have not been able to locate this workaround. Can anyone provide that workaround to me?

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

I am also interested in a way to implement the functionality of nameof in .NET 2.0 if that is possible.

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

8 个解决方案

#1


77  

This code basically does that:

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

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

(Of course it is 3.5 code...)

(当然是3.5代码...)

#2


6  

While reshefm and Jon Skeet show the proper way to do this using expressions, it should be worth noting there's a cheaper way to do this for method names:

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

Wrap a delegate around your method, get the MethodInfo, and you're good to go. Here's an example:

绕过你的方法包装一个委托,得到MethodInfo,你就可以了。这是一个例子:

private void FuncPoo()
{
}

...

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

Unfortunately, this works only for methods; it does not work for properties, as you cannot have delegates to property getter or setter methods. (Seems like a silly limitation, IMO.)

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

#3


4  

The workaround is to use an expression tree, and to take that expression tree apart to find the relevant MemberInfo. There's slightly more detail and comment in this note (although not the code to pull out the member - that's in another SO question somewhere, I believe).

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

Unfortunately as expression trees don't exist in .NET 2.0, there's really no equivalent.

不幸的是,因为.NET 2.0中不存在表达式树,所以实际上没有等价物。

One solution to avoid typos is to have a set of accessors which fetch the relevant PropertyInfo for a particular property, and unit test them. That would be the only place which had the string in it. This would avoid duplication and make refactoring easier, but it's a bit draconian.

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

#4


4  

An extension to what reshefm did, that simplified the usage of the nameof() operator, and gives the names of methods and class members and methods as well:

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

To use it:

要使用它:

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


4  

Unless someone changes their mind, the nameof operator looks like it's coming in C# 6. Here are the design meeting notes about it:

除非有人改变主意,否则操作员的名字看起来就像是在C#6中出现。以下是有关它的设计会议记录:

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

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

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

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

#6


2  

The accepted solution is nice, simple and elegant.

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

However, building an expression tree is expensive, and I need the whole property path.

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

So I changed it a bit. It is not elegant at all, but it is simple and works well in most cases:

所以我改了一下。它根本不优雅,但它很简单,在大多数情况下效果很好:

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

Example:

例:

? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"

#7


1  

This is part of the language in C# 6.0

这是C#6.0语言的一部分

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

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

#8


1  

The answer from reshefm is pretty good, but this is a little bit simpler API IMO:

来自reshefm的答案相当不错,但这是一个有点简单的API IMO:

Usage example: NameOf.Property(() => new Order().Status)

用法示例: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;
    }
  }
}

Full code is here: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

完整代码在这里:http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

#1


77  

This code basically does that:

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

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

(Of course it is 3.5 code...)

(当然是3.5代码...)

#2


6  

While reshefm and Jon Skeet show the proper way to do this using expressions, it should be worth noting there's a cheaper way to do this for method names:

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

Wrap a delegate around your method, get the MethodInfo, and you're good to go. Here's an example:

绕过你的方法包装一个委托,得到MethodInfo,你就可以了。这是一个例子:

private void FuncPoo()
{
}

...

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

Unfortunately, this works only for methods; it does not work for properties, as you cannot have delegates to property getter or setter methods. (Seems like a silly limitation, IMO.)

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

#3


4  

The workaround is to use an expression tree, and to take that expression tree apart to find the relevant MemberInfo. There's slightly more detail and comment in this note (although not the code to pull out the member - that's in another SO question somewhere, I believe).

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

Unfortunately as expression trees don't exist in .NET 2.0, there's really no equivalent.

不幸的是,因为.NET 2.0中不存在表达式树,所以实际上没有等价物。

One solution to avoid typos is to have a set of accessors which fetch the relevant PropertyInfo for a particular property, and unit test them. That would be the only place which had the string in it. This would avoid duplication and make refactoring easier, but it's a bit draconian.

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

#4


4  

An extension to what reshefm did, that simplified the usage of the nameof() operator, and gives the names of methods and class members and methods as well:

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

To use it:

要使用它:

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


4  

Unless someone changes their mind, the nameof operator looks like it's coming in C# 6. Here are the design meeting notes about it:

除非有人改变主意,否则操作员的名字看起来就像是在C#6中出现。以下是有关它的设计会议记录:

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

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

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

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

#6


2  

The accepted solution is nice, simple and elegant.

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

However, building an expression tree is expensive, and I need the whole property path.

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

So I changed it a bit. It is not elegant at all, but it is simple and works well in most cases:

所以我改了一下。它根本不优雅,但它很简单,在大多数情况下效果很好:

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

Example:

例:

? Nameof<DataGridViewCell>.Property(c => c.Style.BackColor.A);
"Style.BackColor.A"

#7


1  

This is part of the language in C# 6.0

这是C#6.0语言的一部分

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

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

#8


1  

The answer from reshefm is pretty good, but this is a little bit simpler API IMO:

来自reshefm的答案相当不错,但这是一个有点简单的API IMO:

Usage example: NameOf.Property(() => new Order().Status)

用法示例: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;
    }
  }
}

Full code is here: http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs

完整代码在这里:http://agiledesignutilities.codeplex.com/SourceControl/changeset/view/b76cefa4234a#GeneralPurpose/NameOf.cs