【问题标题】:Closure in predicates with delegates unknown at compile-time在编译时代理未知的谓词中的闭包
【发布时间】:2011-09-16 12:01:49
【问题描述】:

我有以下场景:

一种方法,用于获取某个类别中低于某个高度阈值的人员列表。我正在使用 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)
{

}

但是,这里的闭包成为问题,因为我无法将变量传递给谓词函数。我可以使一个委托函数内联(不使用 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


    【解决方案1】:

    这不适合你吗?

    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)
    {
    
    }
    

    【讨论】:

    • 现在我想起来了,我把一件简单的事情变成了一件复杂的事情。从某种意义上说,即使使用可变数量的闭包变量,我也需要它来工作,所以数组就可以了,但我确实不需要扩展 Where 方法。不知道我在想什么。
    • 很高兴它有帮助。即使我不知道您在想什么,即我不太了解Delegates,也无法理解代码示例。这可能就是为什么我能够找到更简单的解决方案:)
    • 好吧,我的“解决方案”中的代码只是为了避免将 lambda 作为谓词。但是我没有意识到,如果通过在 lambda 中调用委托,我首先可以达到我想要的外部方法作为谓词,那么 lambda 没有任何问题。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2018-02-26
      • 1970-01-01
      • 2012-05-14
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多