通过lambda表达式传入时,是否有更好的方法来获取属性名称?这是我现在拥有的 .
例如 .
GetSortingInfo<User>(u => u.UserId);
只有当属性是字符串时,它才能将其作为元素表达式进行处理 . 因为并非所有属性都是字符串我必须使用对象,但它会为那些返回一个单一表达式 .
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
19 回答
我在ObjectStateEntry上创建了一个扩展方法,以便能够以类型安全的方式标记属性(Entity Framework POCO类)的属性,因为默认方法只接受字符串 . 这是我从酒店获取名称的方式:
我最近做了一个非常类似的事情来制作一个类型安全的OnPropertyChanged方法 .
这是一个方法,它将返回表达式的PropertyInfo对象 . 如果表达式不是属性,则抛出异常 .
使用
source
参数,以便编译器可以对方法调用进行类型推断 . 您可以执行以下操作我正在为C#6前项目使用扩展方法,对于那些以C#6为目标的项目使用nameof() .
我称之为:
它适用于字段和属性 .
我完成了类似于下面方法的
INotifyPropertyChanged
实现 . 这里的属性存储在下面显示的基类的字典中 . 当然并不总是希望使用继承,但对于视图模型,我认为它是可接受的,并在视图模型类中提供非常干净的属性引用 .更复杂的基类如下所示 . 它处理从lambda表达式到属性名称的转换 . 请注意,属性实际上是伪属性,因为只使用了名称 . 但它对视图模型和视图模型上的属性的引用看起来是透明的 .
现在在C#6你可以像这样使用nameof
nameof(User.UserId)
这有很多好处,其中包括compile time,而不是运行时 .
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
这是另一个答案:
这会处理成员和一元表达式 . 不同之处在于,如果表达式表示值类型,则将获得
UnaryExpression
,而如果表达式表示引用类型,则将获得MemberExpression
. 可以将所有内容强制转换为对象,但必须将值类型装箱 . 这就是UnaryExpression存在的原因 . Reference.对于可读性(@Jowen),这是一个扩展的等价物:
对于
Array
.Length来说,有一个优势 . 虽然'Length'作为属性公开,但您不能在以前提出的任何解决方案中使用它 .现在示例用法:
如果
PropertyNameFromUnaryExpr
没有检查ArrayLength
,"someArray"将被打印到控制台(编译器似乎生成直接访问支持Length字段,作为优化,即使在Debug中,因此特殊情况) .这是另一种方式得到PropertyInfo基于this answer.它消除了对象实例的需要 .
它可以像这样调用:
我发现一些深入到
MemberExpression
/UnaryExpression
的suggested answers不会捕获嵌套/子属性 .ex)
o => o.Thing1.Thing2
返回Thing1
而不是Thing1.Thing2
.如果您尝试使用EntityFramework
DbSet.Include(...)
,这种区别很重要 .我发现只是解析
Expression.ToString()
似乎工作正常,而且相对较快 . 我将它与UnaryExpression
版本进行了比较,甚至将ToString
从Member/UnaryExpression
中移除,看看它是否更快,但差异可以忽略不计 . 如果这是一个可怕的想法,请纠正我 .扩展方法
(检查分隔符可能甚至是过度杀伤)
演示(LinqPad)
演示比较代码 - https://gist.github.com/zaus/6992590
我正在玩同样的事情,并努力工作 . 它没有经过全面测试,但似乎处理了值类型的问题(你遇到的unaryexpression问题)
我更新了@Cameron's answer以对
Convert
类型的lambda表达式进行一些安全检查:如果你想获得多个字段我会离开这个函数:
从.NET 4.0开始,您可以使用ExpressionVisitor查找属性:
以下是您使用此访问者的方式:
好吧,没有必要打电话给
.Name.ToString()
,但广泛的是关于它,是的 . 您可能需要的唯一考虑因素是x.Foo.Bar
是否应返回"Foo","Bar"或异常 - 即您是否需要迭代 .(重新评论)有关灵活排序的更多信息,请参阅here .
这是获取struct / class / interface / delegate / array的fields / properties / indexers / methods / extension methods / delegates的字符串名称的一般实现 . 我已经测试了静态/实例和非泛型/通用变体的组合 .
这个东西也可以用简单的
while
循环编写:我喜欢递归方法,虽然第二个可能更容易阅读 . 人们可以称之为:
打印最后一个成员 .
注意:
如果链接表达式如
A.B.C
,则返回"C" .这不适用于
const
,数组索引器或enum
(不可能涵盖所有情况) .这是method proposed by Cameron的更新 . 第一个参数不是必需的 .
您可以执行以下操作:
扩展方法:
您可以:
我发现你可以做的另一种方法是强烈输入源和属性,并明确推断lambda的输入 . 不确定这是否是正确的术语,但这是结果 .
然后这样称呼它 .
并且它有效 .
谢谢大家 .
使用C#7模式匹配:
例: