我创建了一个看起来像这样的谓词:
p.Name.Contains("Saw")
我有以下代码:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue)
{
PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName);
// ListOfProducts.Where(p => p.Contains(propertyValue))
ParameterExpression pe = Expression.Parameter(typeof(T), "p");
MemberExpression memberExpression = Expression.MakeMemberAccess(pe, propertyInfo);
MethodInfo methodInfo = typeof (string).GetMethod("Contains", new Type[] {typeof (string)});
ConstantExpression constantExpression = Expression.Constant(propertyValue, typeof(string));
// Predicate Body - p.Name.Contains("Saw")
Expression call = Expression.Call(memberExpression, methodInfo, constantExpression);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, pe);
return lambda;
}
但我想将谓词更改为:
p.Name.ToLower().Contains("Saw")
我空白了。我知道我必须在定义MethodInfo的地方添加一些内容。
有人有建议吗?
答案 0 :(得分:4)
不是手动构造整个表达式而只是因为一个小小的部分是动态的,你可以使用常规的lambda来定义所有实际上是静态的内容,然后只需要替换一下那不是。
具体来说,您可以使用的一般策略是使用一个带有参数的lambda来表示您的小动态位,然后在常规lambda中使用它,然后用动态构造的表达式替换该参数的所有实例:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
string propertyName, string propertyValue)
{
Expression<Func<string, bool>> e = s => s.ToLower().Contains(propertyValue);
var parameter = Expression.Parameter(typeof(T));
var property = Expression.PropertyOrField(parameter, propertyName);
var body = e.Body.Replace(e.Parameters[0], property);
return Expression.Lambda<Func<T, bool>>(body, parameter);
}
这不仅简化了您的原始代码,而且对此静态代码进行其他更改就像编辑任何常规的旧C#代码一样简单,而不是要求所有的表达式操作,以及复杂性(和静态类型的丢失)随之而来。
此解决方案使用以下方法将一个表达式的所有实例替换为另一个:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
另一种让事情变得更高级的方法是编写一个Compose
方法,让你轻松编写表达式。从概念上讲,我们将有两个lambda,我们想要创建一个lambda来表示调用一个并将结果传递给另一个,然后返回结果:
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
这或多或少使用了我们上面使用的相同策略,但是它概括了它,而不是特殊地将它包含在你的特定表达式中。
然后我们在将各块放在一起之前有一个剩余的辅助方法;创建一个表示访问属性的方法的方法:
public static Expression<Func<T, string>> MemberSelector<T>(string propertyName)
{
var param = Expression.Parameter(typeof(T));
var body = Expression.PropertyOrField(param, propertyName);
return Expression.Lambda<Func<T, string>>(body, param);
}
使用这两个辅助方法(不依赖于任何特定情况),我们现在可以构建我们想要的lambda,而无需任何自定义构建的表达式操作:
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(
string propertyName, string propertyValue)
{
return MemberSelector<T>(propertyName)
.Compose(prop => prop.ToLower().Contains(propertyValue));
}
答案 1 :(得分:2)
您必须获取ToLower
方法的表达式,然后在Contains
表达式
private static Expression<Func<T, bool>> BuildContainsPredicate<T>(string propertyName, string propertyValue)
{
PropertyInfo propertyInfo = typeof (T).GetProperty(propertyName);
ParameterExpression pe = Expression.Parameter(typeof(T), "p");
MemberExpression memberExpression = Expression.MakeMemberAccess(pe, propertyInfo);
//ToLower expression
MethodInfo toLowerMethodInfo = typeof (string).GetMethod("ToLower", new Type[]{});
Expression toLowerCall = Expression.Call(memberExpression, toLowerMethodInfo);
MethodInfo containsMethodInfo = typeof (string).GetMethod("Contains", new Type[] {typeof (string)});
ConstantExpression constantExpression = Expression.Constant(propertyValue, typeof(string));
// Pass ToLowerCall to
Expression call = Expression.Call(toLowerCall, containsMethodInfo, constantExpression);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, pe);
return lambda;
}
答案 2 :(得分:1)
关于安德烈的答案,这基本上是我在Servy的初步评论之后提出的,我想发布我想出的内容:
for (var i = 0; i < collection.length; i++) {
var result= callback(collection[i], i, collection);
if (result) return result;
}