在 C# 中缺少用于类型安全数据绑定的“nameof”运算符的解决方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/301809/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Workaround for lack of 'nameof' operator in C# for type-safe databinding?
提问by Paul Kapustin
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"
.
nameof
在 C# 中包含运算符的想法很多。作为此运算符如何工作的示例,nameof(Customer.Name)
将返回 string "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.
nameof
如果可能的话,我也对在.NET 2.0 中实现功能的方法感兴趣。
采纳答案by reshefm
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码了...)
回答by Jon Skeet
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
特定属性的相关信息,并对它们进行单元测试。那将是唯一有字符串的地方。这将避免重复并使重构更容易,但它有点严厉。
回答by Judah Gabriel Himango
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,这似乎是一个愚蠢的限制。)
回答by Ad P.
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.
}
}
回答by Sergey
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
回答by Ronnie Overby
Unless someone changes their mind, the nameof
operator looks like it's coming in C# 6. Here are the design meeting notes about it:
除非有人改变主意,否则nameof
运算符看起来像是在 C# 6 中出现的。以下是有关它的设计会议笔记:
https://roslyn.codeplex.com/discussions/552376
https://roslyn.codeplex.com/discussions/552376
回答by kotpal
This is part of the language in C# 6.0
这是 C# 6.0 语言的一部分
回答by Larry
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"