我在这个谓词链中缺少什么?

Dan*_*Tao 10 .net c# linq delegates predicate

注意:在发布这个问题之前,我发现有一个更好的方法来做我想要完成的事情(我觉得它很愚蠢):

IEnumerable<string> checkedItems = ProductTypesList.CheckedItems.Cast<string>();
filter = p => checkedItems.Contains(p.ProductType);
Run Code Online (Sandbox Code Playgroud)

好的,是的,我已经意识到这一点.但是,无论如何,我发布了这个问题,因为我仍然不明白为什么我(愚蠢地)试图做的事情不起作用.


我觉得这很容易.原来它让我很头疼.

基本思路:显示ProductType在a中检查其属性值的所有项目CheckedListBox.

实施:

private Func<Product, bool> GetProductTypeFilter() {
    // if nothing is checked, display nothing
    Func<Product, bool> filter = p => false;

    foreach (string pt in ProductTypesList.CheckedItems.Cast<string>()) {
        Func<Product, bool> prevFilter = filter;
        filter = p => (prevFilter(p) || p.ProductType == pt);
    }

    return filter;
}
Run Code Online (Sandbox Code Playgroud)

但是,如果在ProductTypesList(a CheckedListBox)中检查了"权益"和"ETF"项目.然后由于某种原因,以下代码仅返回"ETF"类型的产品:

var filter = GetProductTypeFilter();
IEnumerable<Product> filteredProducts = allProducts.Where(filter);
Run Code Online (Sandbox Code Playgroud)

我猜想它可能与一些自我引用的混乱有关filter,其本质上是本身其他东西.而且我认为可能会使用......

filter = new Func<Product, bool>(p => (prevFilter(p) || p.ProductType == pt));
Run Code Online (Sandbox Code Playgroud)

......会做的,但没有这样的运气.任何人都能看到我在这里失踪的东西吗?

LBu*_*kin 9

我相信你在这里有一个修改过的闭包问题.pt参数绑定到lambda表达式,但随着循环的进行而更改.重要的是要意识到在lambda中引用变量的时候它是捕获的变量,而不是变量的值.

在循环中,这具有非常显着的分支 - 因为循环变量正在改变,而不是重新定义.通过在循环内创建变量,您将为每次迭代创建一个新变量 - 然后使lambda独立地捕获每个变量.

期望的实施将是:

foreach (string pt in ProductTypesList.CheckedItems.Cast<string>()) {
    string ptCheck = pt;
    Func<Product, bool> prevFilter = filter;
    filter = p => (prevFilter(p) || p.ProductType == ptCheck);
}
Run Code Online (Sandbox Code Playgroud)

Eric Lippert撰写了有关此特定情况的文章:

另外,请参阅访问修改闭包(2)的问题,以便更好地解释闭包变量会发生什么.博客The Old New Thing上还有一系列文章,对此有一个有趣的观点: