【发布时间】:2011-09-16 12:01:49
【问题描述】:
我有以下场景:
一种方法,用于获取某个类别中低于某个高度阈值的人员列表。我正在使用 Where 扩展方法将它们过滤掉。由于谓词依赖于一些外部数据,classId 和 height 变量成为 lambda 闭包的一部分。
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(p => p.Height <= height && p.ClassId == classId).ToList();
}
问题是有时谓词变得复杂所以我想将它提取到另一个方法:
public List<Person> getPeopleFromClassLowerThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isLowerThan).ToList();
}
private bool isLowerThan(Person person)
{
}
但是,这里的闭包成为问题,因为我无法将变量传递给谓词函数。我可以使一个委托函数内联(不使用 lambda),使谓词看起来像一个函数,但是这个函数也必须与 Where() 调用使用相同的方法。在大多数情况下都可以,但有时我希望将外部函数(在编译时未知)称为 Where 谓词。
我最接近解决方案的是:
public delegate bool ExtendedPredicate<T, TArg>(T argument, TArg[] arguments);
public static class Extended
{
public static IEnumerable<T> Where<T, TArg>(this IEnumerable<T> collection, ExtendedPredicate<T, TArg> predicate, params TArg[] arguments)
{
foreach (T item in collection)
{
if (predicate(item, arguments))
yield return item;
}
yield break;
}
}
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isFromClassShorterThanLimit, classId, height).ToList();
}
public bool isFromClassShorterThanLimit(Person person, int[] arguments)
{
return person.Height <= arguments[0] && person.ClassId == arguments[1];
}
但我觉得它有点太窄了,而且对于每个扩展方法都必须编写一个新的重载。除此之外,我猜 EntityFramework 将无法将此类表达式转换为合理的 SQL 语句(对此不确定,我不是 EF 如何从 lambda 生成 SQL 的专家,但这是一个疯狂的猜测)。 (可能提供也可能不提供可变数量的参数)
问题:
我想知道的是,是否有任何聪明的方法可以通过常规委托(不强制将编译时定义的函数用作谓词)来实现这一点,而无需像上面显示的那样黑客攻击?
【问题讨论】:
标签: c# delegates lambda closures predicate