动态递归lambda表达式
本文关键字:表达式 lambda 递归 动态 | 更新日期: 2023-09-27 17:57:33
我想创建动态lambda表达式,这样我就可以使用一组过滤参数来过滤列表。这就是我目前所拥有的:
该表达式使用以下方法构建,其中T是列表的对象类型
public static Expression<Func<T, bool>> GetExpression<T>(IList<DynamicFilter> filters)
{
if (filters.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
Expression exp = null;
if (filters.Count == 1)
exp = GetExpression<T>(param, filters[0]);
[...]
return Expression.Lambda<Func<T, bool>>(exp, param);
}
private static Expression GetExpression<T>(ParameterExpression param, DynamicFilter filter)
{
MemberExpression member = Expression.Property(param, filter.PropertyName);
ConstantExpression constant = Expression.Constant(filter.Value);
[...]
return Expression.Call(member, filterMethod, constant);
}
然后我打电话给
List<Example> list = ...;
var deleg = ExpressionBuilder.GetExpression<Example>(dynFiltersList).Compile();
list = list.Where(deleg).ToList();
对于只包含简单类型的对象,这与预期的一样有效,但如果其中包含复杂类型,则代码将不再有效。例如,假设我在example类中有一个自定义类型Field的成员,Field有一个字符串属性Value。如果filter.PropertyName
是"Field0"(类型为Field),代码会正常工作,但如果我有"Field0.Value",我会得到一个明显的错误,指出类Example中没有名为"Field0.Value"的属性。
我尝试修改表达式构建方法,如下所示:
MemberExpression member = null;
if (filter.PropertyName.Contains('.'))
{
string[] props = filter.PropertyName.Split('.');
ParameterExpression param1 = Expression.Parameter(typeof(T).GetProperty(props[0]).PropertyType, "t1");
member = Expression.Property(param1, props[0]);
}
else
{
member = Expression.Property(param, filter.PropertyName);
}
但后来我在编译表达式时出现了Lambda parameter not in scope
错误。我有点理解我为什么会犯这个错误,但我不知道如何让它发挥作用。
最重要的是,在形成MemberExpression时,我需要使表达式构建方法递归工作。我最终需要获得一个list = list.Where(deleg).ToList();
,它可以翻译成类似于list = list.Where(obj => obj.Field0.Value == 'something').ToList();
的东西
我刚开始研究表情,所以我在这方面真的不太了解,但如果有任何帮助,我将不胜感激。
感谢
我意识到这是一篇相当古老的帖子,但我遇到了完全相同的问题,并在Mark Gravell在这里发布的答案中找到了一些接近的东西。我只是稍微修改了一下以满足我的需求,下面是结果:
private Expression GetDeepProperty(Expression parameter, string property)
{
var props = property.Split('.');
var type = parameter.Type;
var expr = parameter;
foreach (var prop in props)
{
var pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
return expr;
}
实施:
var method = typeof (string).GetMethod("Contains", new Type[] {typeof (string)}, null);
var lambdaParameter = Expression.Parameter(typeof(TEntity), "te");
var filterExpression = Expression.Lambda<Func<TEntity, bool>> (
filters.Select(filter => Expression.Call(GetDeepProperty(lambdaParameter, filter.Property),
method,
Expression.Constant(filter.Value))).
Where(exp => exp != null).
Cast<Expression>().
ToList().
Aggregate(Expression.Or), lambdaParameter);
我正在尝试解决问题
这样我就可以使用一组过滤参数来过滤列表
不是通过使用CCD_ 5而是通过使用通用Filter类。
public class Filter<T> where T: class
{
private readonly Predicate<T> criteria;
public Filter(Predicate<T> criteria)
{
this.criteria = criteria;
}
public bool IsSatisfied(T obj)
{
return criteria(obj);
}
}
首先我们需要上课。
public class Player
{
public string Name { get; set; }
public int Level { get; set; }
public enum Sex { Male, Female, Other };
public Weapon Weapon { get; set; }
}
public class Weapon
{
public string Name { get; set; }
public int MaxDamage { get; set; }
public int Range { get; set; }
public WeaponClass Class { get; set; }
public enum WeaponClass { Sword, Club, Bow }
}
然后我们需要一个对象列表。
var graywand = new Weapon { Name = "Graywand", MaxDamage = 42, Range = 1, Class = Weapon.WeaponClass.Sword };
var scalpel = new Weapon { Name = "Scalpel", MaxDamage = 33, Range = 1, Class = Weapon.WeaponClass.Sword };
var players = new List<Player> {
new Player { Name = "Fafhrd", Level = 19, Weapon = graywand },
new Player { Name = "Gray Mouser", Level = 19, Weapon = scalpel },
new Player { Name = "Freddy", Level = 9, Weapon = graywand },
new Player { Name = "Mouse", Level = 8, Weapon = scalpel}
};
然后,让我们创建几个过滤器,并将它们添加到列表中。
var powerfulSwords = new Filter<Player>(p => p.Weapon.MaxDamage>35);
var highLevels = new Filter<Player>(p => p.Level>15);
var filters = new List<Filter<Player>>();
filters.Add(powerfulSwords);
filters.Add(highLevels);
最后通过这些过滤器过滤列表
var highLevelAndPowerfulSwords = players.Where(p => filters.All(filter => filter.IsSatisfied(p)));
var highLevelOrPowerfulSwords = players.Where(p => filters.Any(filter => filter.IsSatisfied(p)));
只有"Fafhard"将出现在highLevelAndPowerfulSwords
中,而highLevelOrPowerfulSwords
将包含除"Mouse"之外的所有玩家。
查看ExpressionVisitor,如下所述:替换表达式正文中的参数名称
您可以为filter中的每个元素生成一个表达式,并使用以下方法将它们组合为一个表达式:
public static Expression<Func<T, K>> CombineAnd<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
{
ParameterExpression firstParameter = a.Parameters.First();
Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
return Expression.Lambda<Func<T, K>>(Expression.And(a.Body, b1.Body), firstParameter);
}
public static Expression<Func<T, K>> CombineOr<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
{
ParameterExpression firstParameter = a.Parameters.First();
Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
return Expression.Lambda<Func<T, K>>(Expression.Or(a.Body, b1.Body), firstParameter);
}