如何将Linq Expression<Func<object,bool>>编译为Func<object,bool>并解决ArgumentException
错误原因
报错的核心是表达式体的返回类型不匹配:
CreateExpression方法最后返回的是LambdaExpression类型,你在方法内部已经套了一层Lambda,这个表达式的计算结果是Func<object, bool>委托,不是你需要的bool类型的条件结果- 你在
CompileExpression中又把这个返回委托的表达式作为Lambda体,再包了一层Expression.Lambda<Func<object, bool>>,等于外层Lambda的返回值是委托,和你指定的返回类型bool冲突,和异常提示的内容完全对应。
修复方案
提供两种改法,第一种逻辑更简洁、可维护性更高:
改法1:去掉CreateExpression里多余的Lambda包装,直接返回条件表达式
注意要保证两层方法用的是同一个ParameterExpression实例,不能重复创建参数,否则会出现参数不匹配的问题:
public delegate Expression ComparisonExpressionDelegate(Expression property, Expression value); public class ConditionCompiler { public class TestClass { public string StringProperty1 { get; set; } public string StringProperty2 { get; set; } public string StringProperty3 { get; set; } public int IntProperty1 { get; set; } public double DoubleProperty1 { get; set; } } public static Func<object, bool> Test() { PropertyInfo property = typeof(TestClass).GetProperty(nameof(TestClass.StringProperty1)); object value = "1"; return CompileExpression(property, value); } public static Func<object, bool> CompileExpression(PropertyInfo property, object value) { ParameterExpression obj = Expression.Parameter(typeof(object), "obj"); // 把外层创建的参数实例传到CreateExpression Expression resultExpression = CreateExpression(obj, property, value); Func<object, bool> resultLambda = Expression.Lambda<Func<object, bool>>(resultExpression, obj).Compile(); return resultLambda; } // 新增参数接收外部的ParameterExpression实例 public static Expression CreateExpression(ParameterExpression obj, PropertyInfo property, object value) { bool isGetMethodStatic = property.GetGetMethod().IsStatic; UnaryExpression typedTarget = Expression.Convert(obj, property.DeclaringType); MemberExpression memberAccess = Expression.Property(isGetMethodStatic ? null : typedTarget, property); UnaryExpression boxedGetter = Expression.TypeAs(memberAccess, typeof(object)); ConstantExpression valueExpression = Expression.Constant(value); ComparisonExpressionDelegate comparisonExpressionDelegate = ToString_Equals_ToString_Expression; Expression conditionExpression = comparisonExpressionDelegate(boxedGetter, valueExpression); // 直接返回bool类型的条件表达式,去掉多余的Lambda包装 return conditionExpression; } private static ComparisonExpressionDelegate ToString_Equals_ToString_Expression = (property, value) => Expression.Equal( Expression.Call( typeof(string), nameof(String.Compare), null, Expression.Call(property, "ToString", null), value, Expression.Constant(StringComparison.OrdinalIgnoreCase) ), Expression.Constant(0) ); }
改法2:直接编译CreateExpression返回的Lambda
不需要在CompileExpression里重复套Lambda,直接编译CreateExpression返回的Lambda即可:
public static Func<object, bool> CompileExpression(PropertyInfo property, object value) { LambdaExpression resultExpression = (LambdaExpression)CreateExpression(property, value); return (Func<object, bool>)resultExpression.Compile(); }
内容的提问来源于stack exchange,提问作者Michael Wagner
相关产品推荐
相关产品推荐

