在编译时代表未知的谓词中的闭包

时间:2011-09-16 12:01:50

标签: c# delegates lambda closures predicate

我有以下情况:

一种方法,用于获取某个类的人员列表,这些人员的列表短于某个高度阈值。我正在使用Where扩展方法来过滤它们。由于谓词依赖于某些外部数据,因此classIdheight变量成为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)
{

}

然而,这里的闭包成为问题,因为我无法将变量传递给谓词函数。我可以使内联的委托函数(不使用lambdas)使谓词看起来像一个函数,但是此函数也必须与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如何从lambdas生成SQL的专家,但这是一个疯狂的猜测)。 (可能提供或不提供可变数量的参数)

问题:

我想知道的是,是否有任何聪明的方法可以通过常规委托(不强制编译时定义的函数作为谓词)来实现这一点,而不会出现如上所示的黑客攻击?

2 个答案:

答案 0 :(得分:2)

这不适合你吗?

public List<Person> getPeopleFromClassLowerThanLimit(int classId, int height)
{
    Database db = new Database();
    return db.Persons.Where(p => isLowerThan(p, classId, height)).ToList();
}

private bool isLowerThan(Person person, int classId, int height)
{

}

答案 1 :(得分:0)

为什么不简单地将你的方法称为普通方法的谓词:

public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
    Database db = new Database();
    return db.Persons.Where(x=>isFromClassShorterThanLimit(classId, height)).ToList();
}