将表达式引用解析为实际值

问题描述 投票:0回答:1

我想记录表达式中的实际值,而不是表达式中使用的属性参数的引用。我这里有一个fiddle。https:/dotnetfiddle.net7SNxAq 其中的代码(供后人参考)是。

using System;
using System.Linq.Expressions;

public class Program
{
    public static void Main()
    {
        var input = new Foo
        {
            Shape = "Sphere",
            SizeType = SizeType.Small
        };

        Expression<Func<Foo, bool>> expression = f =>
            f.Hue == Constants.Hues.Red &&
            f.Shape == input.Shape &&
            f.Size == input.SizeType.ToString() &&
            !f.IsDeleted;

        Console.WriteLine(expression);
    }
}

internal class Foo
{
    public string Hue { get; set; }
    public string Shape { get; set; }
    public bool IsDeleted { get; set; }
    public string Size { get; set; }
    public SizeType SizeType { get; set; }
}

internal enum SizeType
{
    Small, Medium, Large
}

internal class Constants
{
    public class Hues
    {
        public static string Red = "#f00";
    }
}

结果是: Console.WriteLine(expression) 是。

f => ((((f.Hue == Hues.Red) AndAlso (f.Shape == value(Program+<>c__DisplayClass0_0).input.Shape)) AndAlso (f.Size == value(Program+<>c__DisplayClass0_0).input.SizeType.ToString())) AndAlso Not(f.IsDeleted))

但我想看到这样的东西:

f => ((((f.Hue == Hues.Red) AndAlso (f.Shape == "Sphere")) AndAlso (f.Size == "Small")) AndAlso Not(f.IsDeleted))

可以这样做吗,还是我错过了表达式的重点?

c# .net lambda expression
1个回答
1
投票

所以需要的是摆脱闭包。为此,需要重写一棵Expression树(要注意,Expression访问者中并不是所有的情况都会考虑)。

public static void Main()
{
    // ...

    var updated = (Expression<Func<Foo, bool>>)
        new ClosureResolver().Visit(expression);

    // Outputs:
    // f => ((((f.Hue == Hues.Red) AndAlso (f.Shape == "Sphere")) AndAlso (f.Size == "Small")) AndAlso Not(f.IsDeleted))
    Console.WriteLine(updated);
}


public class ClosureResolver : ExpressionVisitor
{
    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        if (node.Arguments.Count == 0)
        {
            var objExpr = Visit(node.Object);

            if (objExpr is ConstantExpression objConstExpr)
            {
                var res = node.Method.Invoke(objConstExpr.Value, new object[0]);
                return Expression.Constant(res);
            }
        }

        return base.VisitMethodCall(node);
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        var childExpr = Visit(node.Expression);

        if (childExpr is ConstantExpression constExpr)
        {
            if (node.Member is FieldInfo field)
            {
                var constVal = field.GetValue(constExpr.Value);
                return Expression.Constant(constVal);
            }
            else if (node.Member is PropertyInfo prop)
            {
                var constVal = prop.GetValue(constExpr.Value);
                return Expression.Constant(constVal);
            }
        }

        return base.VisitMember(node);
    }
}

一旦Expression树被重写,它将不再有闭包,所以在以下情况下它可能会有不同的表现: input 已经改变了。

static void ClosureDemo()
{
    var input = new Foo { Shape = "Sphere" };

    Expression<Func<Foo, bool>> expr = f =>
        f.Shape == input.Shape;

    var updated = (Expression<Func<Foo, bool>>) 
        new ClosureResolver().Visit(expr);

    var fn = expr.Compile();
    var updatedFn = updated.Compile();


    Console.WriteLine(fn(input)); // True
    Console.WriteLine(updatedFn(input)); // True

    input.Shape = "Cube";

    Console.WriteLine(fn(input)); // True
    Console.WriteLine(updatedFn(input)); // False
}
© www.soinside.com 2019 - 2024. All rights reserved.