如何使用for循环迭代链接IEnumerable.Where()?

Xåp*_* - 1 .net c# linq ienumerable lambda

考虑以下代码(尽管有点人为,但它是对现实世界程序的一个重大简化):

string[] strings = { "ab", "abcd", "abc", "ac", "b", "abcde", "c", "bc" };
string[] filters = { "a", "b", "c" };

// iteratively apply each filter
IEnumerable<string> filteredStrings = strings.ToArray();
foreach (string filter in filters)
{
    // in my real-world program lots of processing and stuff
    // happens here, hence why i need the enclosing foreach loop

    filteredStrings = filteredStrings.Where(s => s.Contains(filter));
}
Run Code Online (Sandbox Code Playgroud)

如您所见,代码迭代地将字符串数组过滤到较小的字符串集.当for-each循环结束时,filteredStrings应该是通过所有过滤器的字符串子集.在这个例子中,那将是:

{ "abcd", "abc", "abcde" }
Run Code Online (Sandbox Code Playgroud)

但是,我得到的输出是:

{ "abcd", "abc", "ac", "abcde", "c", "bc" }
Run Code Online (Sandbox Code Playgroud)

它似乎只是过滤掉那些不包含的字符串"c",我认为它与最后一个过滤器有关.我想我一定不能以IEnumerable.Where()正确的方式进行链接.这里发生了什么,我怎样才能获得正确的输出?

是的,根据我的代码中的注释,foreach循环需要保持不变.

Dav*_*Yaw 9

where委托不以您期望的方式捕获局部变量.变量filter正在变化,并且它使用了最后的结果,因为Linq做了懒惰的评估.

将迭代变量复制到本地var,我相信它将捕获您期望的方式.

foreach (string filter in filters)
{
    string localFilter = filter;
    filteredStrings = filteredStrings.Where(s => s.Contains(localFilter));
}
Run Code Online (Sandbox Code Playgroud)

  • 在C#5中,这将起作用.在C#4中,它不仅仅像你说的那样.参见[Eric Lippert:关闭循环变量被认为有害](http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful. ASPX) (5认同)