访问成员表达式的值
Posted
技术标签:
【中文标题】访问成员表达式的值【英文标题】:Access the value of a member expression 【发布时间】:2011-02-06 16:21:02 【问题描述】:如果我有产品。
var p = new Product Price = 30 ;
我有以下 linq 查询。
var q = repo.Products().Where(x=>x.Price == p.Price).ToList()
在 IQueryable 提供程序中,我为 p.Price 返回了一个 MemberExpression,其中包含一个常量表达式,但是我似乎无法从中获取值“30”。
更新 我已经尝试过,但它似乎不起作用。
var memberExpression = (MemberExpression)GetRootConstantExpression(m);
var fi = (PropertyInfo)memberExpression.Member;
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null);
干杯。
【问题讨论】:
【参考方案1】:您可以编译和调用一个 lambda 表达式,其主体是成员访问:
private object GetValue(MemberExpression member)
var objectMember = Expression.Convert(member, typeof(object));
var getterLambda = Expression.Lambda<Func<object>>(objectMember);
var getter = getterLambda.Compile();
return getter();
局部求值是解析表达式树时的常用技术。 LINQ to SQL 在很多地方都可以做到这一点。
【讨论】:
Get this error Expression of type 'System.Double' cannot be used for return type 'System.Object' when it resolve to a double as in the example. 必须添加: var expression = Expression.Convert(member, typeof(object));在函数的第一行通过双重转换修复上述错误! 啊,是的,我有时会忘记,在 C# 是隐式的(如转换)的情况下,您必须对表达式树进行显式处理。我很高兴这对你有用。 太棒了! :) 非常感谢。 注意:如果您有一个 无参数表达式expr
类型为 Expression<Func<T>>
(例如 expr
包含表达式 @987654325 @),然后单线 object exprValue = expr.Compile()();
就可以了。之后将其转换为您需要的类型。在某些情况下很有用。【参考方案2】:
MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
Expression.Lambda(right).Compile().DynamicInvoke();
【讨论】:
获得结果的最快、最简洁的方式。 不敢相信涉及DynamicInvoke
的东西可能是最快 @iggymoran 你测试了吗?或者你的意思是打字最快? ;)
打字速度最快,最容易理解到底发生了什么。 DynamicInvoke 最终使用反射来执行它,并不是世界上最快的东西。 Bryan Watts 的回答通过获取一个函数并执行它(只需调用)来解决这个问题。当我第一次想到这个答案时,更容易理解发生了什么。
如果可以的话,我会给你+10 :) 太棒了【参考方案3】:
常量表达式将指向编译器生成的捕获类。我没有包括决策点等,但这里是如何从中获得 30:
var p = new Product Price = 30 ;
Expression<Func<Product, bool>> predicate = x => x.Price == p.Price;
BinaryExpression eq = (BinaryExpression)predicate.Body;
MemberExpression productToPrice = (MemberExpression)eq.Right;
MemberExpression captureToProduct = (MemberExpression)productToPrice.Expression;
ConstantExpression captureConst = (ConstantExpression)captureToProduct.Expression;
object product = ((FieldInfo)captureToProduct.Member).GetValue(captureConst.Value);
object price = ((PropertyInfo)productToPrice.Member).GetValue(product, null);
price
现在是 30
。请注意,我假设Price
是一个属性,但实际上您会编写一个处理属性/字段的GetValue
方法。
【讨论】:
如果你在对象中有另一个层次的嵌套,会有什么改变吗?例如。 p.Product.Price @Schotime - 确实如此。要以通用方式处理此问题,请参阅此处的Evaluate
和 TryEvaluate
:code.google.com/p/protobuf-net/source/browse/trunk/…
@MarcGravell 哪个更快:编译MemberExpression
然后评估它,或者达到它的PropertyInfo/FieldInfo
然后像TryEvaluate
一样评估它?
@AshrafSabry 这取决于您执行了多少次,以及您是否正在重用委托【参考方案4】:
如果你有课:
public class Item
public int Id get; set;
以及对象的一个实例:
var myItem = new Item Id = 7 ;
您可以使用以下代码使用表达式获取 Id 的值:
Expression<Func<Item, int>> exp = x => x.Id;
var me = exp.Body as MemberExpression;
var propInfo = me.Member as PropertyInfo;
var myValue = propInfo.GetValue(myItem, null);
myValue 将包含“7”
【讨论】:
value
是保留标识符
@NickGallimore 谢谢,好地方!我已经相应地更新了示例以删除它【参考方案5】:
使用Expression.Lambda(myParameterlessExpression).Compile().Invoke()
有几个缺点:
.Compile()
是慢。即使是小的表达式片段也可能需要几毫秒才能完成。 Invoke
-调用之后的速度非常快,简单的算术表达式或成员访问只需几纳秒。
.Compile()
将生成(发出)MSIL 代码。这听起来可能很完美(并解释了出色的执行速度),但问题是:该代码占用了内存,在应用程序完成之前无法释放,即使 GC 收集了委托引用!
可以完全避免Compile()
以避免这些问题,也可以缓存已编译的委托以重新使用它们。 This 我的小库同时提供Expressions
的解释以及缓存编译,其中表达式的所有常量和闭包都会自动替换为附加参数,这然后重新插入到一个闭包中,该闭包返回给用户。这两个过程都经过充分测试,用于生产,各有利弊,但比 Compile()
快 100 倍以上 - 并避免内存泄漏!
【讨论】:
已编译的委托和相关代码被垃圾回收【参考方案6】:截至 2020 年
此帮助方法将优雅地检索任何表达式值,而无需“编译黑客”:
public static object GetMemberExpressionValue (MemberExpression expression)
// Dependency chain of a MemberExpression is of the form:
// MemberExpression expression
// MemberExpression expression.Expression
// ... MemberExpression expression.[...].Expression
// ConstantExpression expression.[...].Expression.Expression <- base object
var dependencyChain = new List<MemberExpression>();
var pointingExpression = expression;
while (pointingExpression != null)
dependencyChain.Add(pointingExpression);
pointingExpression = pointingExpression.Expression as MemberExpression;
if (!(dependencyChain.Last().Expression is ConstantExpression baseExpression))
throw new Exception(
$"Last expression dependencyChain.Last().Expression of dependency chain of expression is not a constant." +
"Thus the expression value cannot be found.");
var resolvedValue = baseExpression.Value;
for (var i = dependencyChain.Count; i > 0; i--)
var expr = dependencyChain[i - 1];
resolvedValue = new PropOrField(expr.Member).GetValue(resolvedValue);
return resolvedValue;
PropOrField
类:
public class PropOrField
public readonly MemberInfo MemberInfo;
public PropOrField (MemberInfo memberInfo)
if (!(memberInfo is PropertyInfo) && !(memberInfo is FieldInfo))
throw new Exception(
$"nameof(memberInfo) must either be nameof(PropertyInfo) or nameof(FieldInfo)");
MemberInfo = memberInfo;
public object GetValue (object source)
if (MemberInfo is PropertyInfo propertyInfo) return propertyInfo.GetValue(source);
if (MemberInfo is FieldInfo fieldInfo) return fieldInfo.GetValue(source);
return null;
public void SetValue (object target, object source)
if (MemberInfo is PropertyInfo propertyInfo) propertyInfo.SetValue(target, source);
if (MemberInfo is FieldInfo fieldInfo) fieldInfo.SetValue(target, source);
public Type GetMemberType ()
if (MemberInfo is PropertyInfo propertyInfo) return propertyInfo.PropertyType;
if (MemberInfo is FieldInfo fieldInfo) return fieldInfo.FieldType;
return null;
【讨论】:
【参考方案7】:q
的类型为 List<Product>
。该列表没有 Price 属性 - 只有单个产品。
第一个或最后一个产品会有一个价格。
q.First().Price
q.Last().Price
如果您知道集合中只有一个,您也可以使用 Single 将其展平
q.Single().Price
【讨论】:
是的,但最后的.ToList()
将其放入列表中。
无论是 List 还是 IQueryable,您仍然可以使用 First、Last 或 Single - 但不要搞错,repo.Products.ToList()
绝对是 List
你是对的科比。我对这些东西了如指掌,因为 I 确实在尝试解析表达式树。只是稍微复杂一点。
好的,我现在看到你是/想要达到的目标,我从最初的问题中不明白。【参考方案8】:
您可以使用以下内容吗:
var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
【讨论】:
这会起作用,但是如果这不需要发生,那就太好了。 Linq-2-Sql 是否支持我想要实现的语法?【参考方案9】:你到底想完成什么?
因为要访问Price
的值,您必须执行以下操作:
var valueOfPrice = q[0].Price;
【讨论】:
我正在尝试将表达式转换为纯文本,并且需要将 p.Price 评估为“30”以上是关于访问成员表达式的值的主要内容,如果未能解决你的问题,请参考以下文章