假设数组是一个整数数组:将x => array.Contains(x)表达式转换为x => x == 1 || X == 2
var array = new [] { 1, 2 }
而且,我们说有一个对象名一些,与性能:
public class Some
{
public int Id { get; set;}
}
我需要一种方法来转换:
Expression<Func<Some, bool>> exp = x => array.Contains(x.Id)
表达成:
Expression<Func<Some, bool>> exp = x => x.Id == 1 || x.Id == 2
UPDATE
我已经在列表中的扩展方法,从列表生成所需的结果:我所问的是,给定的表达式1我怎么可能将其转换为表达2.我不希望推动其他团队成员使用扩展而不是普通的包含方法。
我的扩展方法:
array.SafeContainsExpression<Some, string>(nameof(Some.Id));
,代码:
public static Expression<Func<TModel, bool>> SafeContainsExpression<TModel, TValue>(
this IEnumerable<TValue> list, string propertyName)
{
var argParam = Expression.Parameter(typeof(TModel), "x");
var selector = Expression.Property(argParam, propertyName);
Expression left = null;
foreach (var value in list)
{
var valueExpression = Expression.Constant(value, typeof(TValue));
var right = Expression.Equal(selector, valueExpression);
if (left == null)
left = right;
left = Expression.OrElse(left, right);
}
return Expression.Lambda<Func<TModel, bool>>(left, argParam);
}
溶液(来自接受的答案)
public class SafeExpressionsVisitor : LinqKit.ExpressionVisitor
{
protected override Expression VisitMethodCall(MethodCallExpression m)
{
if (m.Method.Name == "Contains" && m.Arguments.Count == 2)
{
var list = Expression.Lambda<Func<IEnumerable>>(m.Arguments[0]).Compile()();
var propertyExpression = (MemberExpression)m.Arguments[1];
Expression left = null;
foreach (var value in list)
{
var valueExpression = Expression.Constant(value);
var right = Expression.Equal(propertyExpression, valueExpression);
if (left == null)
{
left = right;
continue;
}
left = Expression.OrElse(left, right);
}
return left;
}
return base.VisitMethodCall(m);
}
}
public class ExpressionTests
{
[Fact]
public void Shoul_Convert_With_Visitor()
{
var array = new[] { 1, 2 };
Expression<Func<A, bool>> exp = x => array.Contains(x.Id);
var safeExp = Expression.Lambda<Func<A, bool>>(
new SafeExpressionsVisitor().Visit(exp.Body),
exp.Parameters);
var func = safeExp.Compile();
Assert.True(func(new A { Id = 1 }));
Assert.True(func(new A { Id = 2 }));
Assert.False(func(new A { Id = 3 }));
}
}
你是什么意思转换?为什么不更换? –
为什么?如果这是某种优化传递,那么最好用'HashSet.Contains'代替它。 –
@SergeyBerezovskiy这是一个伪代码,数组的值在运行时改变。我认为我需要一个'ExpressionVisitor'来获取数组的值,并为每个值从'Expression.Equal'创建表达式,并用'Expression.OrElse'链接它们。但我无法弄清楚如何去做。 –