2010-04-11 68 views
54

如果我有产品。访问成员表达式的值

var p = new Product { Price = 30 }; 

并且我有以下linq查询。

var q = repo.Products().Where(x=>x.Price == p.Price).ToList() 

在一个IQueryable供应商,我得到一个MemberExpression回来含有常量表达式的p.Price,但我似乎无法得到值“30”,从回来。

更新 我试过这个,但它似乎没有工作。

var memberExpression = (MemberExpression)GetRootConstantExpression(m); 
var fi = (PropertyInfo)memberExpression.Member; 
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null); 

干杯。

回答

84

您可以编译和调用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在很多地方都能做到这一点。

+0

获取此错误类型为“System.Double”的表达式不能用于返回类型为“System.Object”的解析为double在我使用的例子中。 – Schotime 2010-04-11 13:12:16

+2

不得不添加: var expression = Expression.Convert(member,typeof(object));在函数的第一行用双重转换修复上述错误! – Schotime 2010-04-11 14:23:58

+0

啊,是的,我有时会忘记你必须明确C#隐含的表达式树(比如转换)。我很高兴这对你有用。 – 2010-04-11 18:18:30

1

q的类型为List<Product>。该清单没有价格属性 - 只有单个产品。

第一个或最后一个产品将有一个价格。

q.First().Price 
q.Last().Price 

如果你知道有一个仅使用在收集你也可以将其压平单

q.Single().Price 
+1

哈啊....没有。 repo.Products()返回一个IQueryable 。 – Schotime 2010-04-11 10:07:32

+0

是的,但是最后的'.ToList()'使它成为一个列表。 – 2010-04-11 10:26:52

+1

无论是List还是IQueryable,您仍然可以使用First,Last或Single - 但不会犯任何错误,'repo.Products.ToList()'绝对是一个列表 – 2010-04-11 10:28:10

0

而且正是你想实现什么?

因为访问的Price的价值,你必须做一些事情,如:

var valueOfPrice = q[0].Price; 
+0

我试图将表达式转换为纯文本,并且需要将p.Price评估为“30” – Schotime 2010-04-11 10:10:16

1

您可以使用以下方法:

var price = p.Price; 
var q = repo.Products().Where(x=>x.Price == price).ToList() 
+0

这将起作用,但如果不需要这样做会很好。 Linq-2-Sql是否支持我试图实现的语法? – Schotime 2010-04-11 12:12:19

23

常量表达式将会指向捕获类由编译器生成。我不包括决策点等等,但这里是如何拿到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

+0

ahhhhh ....我是一个关卡。传说!!! – Schotime 2010-04-11 12:29:38

+0

如果你在对象中有另一个层次的嵌套会有什么变化?例如。 p.Product.Price – Schotime 2010-04-11 12:58:57

+3

@Schotime - 的确如此。要以通用方式处理此问题,请参阅此处的“评估”和“TryEvaluate”:http://code.google.com/p/protobuf-net/source/browse/trunk/protobuf-net.Extensions/ServiceModel/Client/ ProtoClientExtensions.cs – 2010-04-11 15:06:29

26
MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right; 
Expression.Lambda(right).Compile().DynamicInvoke(); 
+0

获取结果的最快,最简洁的方式。 – iggymoran 2012-10-09 15:41:26

+1

不能相信涉及“DynamicInvoke”的东西可以*最快* @iggymoran你测试过吗?或者你的意思是打字最快? ;) – nawfal 2013-06-04 16:52:14

+0

最快的类型和最容易理解到底发生了什么。 DynamicInvoke最终使用反射来执行它,并不是世界上最快的事情。Bryan Watts的答案通过获得func并执行(通过调用)来解决这个问题。当我第一次遇到这个答案时,它更容易理解发生了什么。 – iggymoran 2013-06-06 08:38:16

1

使用Expression.Lambda(myParameterlessExpression).Compile().Invoke()有几个缺点:

  • .Compile()。即使对于小型表达片段,也可能需要几毫秒才能完成。然而,Invoke-调用之后超快,对于简单的算术表达式或成员访问只需要几个纳秒。
  • .Compile()将生成(发出)MSIL代码。这可能听起来很完美(并解释了出色的执行速度),但问题是:代码占用内存,在应用程序完成之前无法释放,即使GC收集了委托引用!

可以完全避免Compile()以避免这些问题或缓存已编译的代表重新使用它们。 This我的小库提供解释Expressions以及缓存编译,其中表达式的所有常量和闭包自动替换为附加参数,然后重新插入闭包中,然后返回给用户。这两个流程都经过良好测试,用于生产,两者都有优点和缺点,但比Compile()快100倍以上 - 并避免内存泄漏!

0

如果你有一个类:

public class Item 
{ 
    public int Id { get; set; } 
} 

和对象的实例:

Expression<Func<Item, int>> exp = x => x.Id; 
var me = exp.Body as MemberExpression; 
var propInfo = me.Member as PropertyInfo; 
var value = propInfo.GetValue(myItem, null); 

var myItem = new Item { Id = 7 }; 

您可以用下面的代码中使用表达得id的值

值将包含“7”