访问成员表达式的值

时间:2010-04-11 09:58:04

标签: c# linq lambda expression-trees

如果我有产品。

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

干杯。

9 个答案:

答案 0 :(得分:97)

您可以编译和调用其主体为成员访问权限的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在很多地方都做到了这一点。

答案 1 :(得分:28)

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

答案 2 :(得分:24)

常量表达式将指向编译器生成的捕获类。我没有列出决策点等,但这里是如何从中得到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方法。

答案 3 :(得分:1)

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

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

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

如果你知道系列中只有一个,你也可以使用Single

来展平它
q.Single().Price

答案 4 :(得分:1)

您可以使用以下内容:

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

答案 5 :(得分:1)

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

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

可以完全避免Compile()以避免这些问题,也可以缓存已编译的代理以重新使用它们。 This我的小库提供了Expressions解释以及缓存编译,其中表达式的所有常量和闭包都被其他参数自动,然后重新插入一个闭包,然后返回给用户。这两个过程都经过了充分测试,在生产中使用,两者各有利弊,但速度比Compile()快100倍以上 - 避免内存泄漏!

答案 6 :(得分:0)

你究竟想要完成什么?

因为要访问Price的值,您必须执行以下操作:

var valueOfPrice = q[0].Price;

答案 7 :(得分:0)

如果你有一个班级:

for(j in 1:length(names(df2))){
  assign(names(df2)[j],df2[j])
}

和对象的实例:

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

您可以使用以下代码使用Expression获取Id的值:

var myItem = new Item { Id = 7 };

值将包含“7”

答案 8 :(得分:-1)

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