我想记录表达式中的实际值,而不是表达式中使用的属性参数的引用。我这里有一个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))
可以这样做吗,还是我错过了表达式的重点?
所以需要的是摆脱闭包。为此,需要重写一棵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
}