LINQ之路 6:延迟执行(Deferred Execution) 笔记
这里刚看的时候不理解。
这个特性在我们通过foreach循环创建查询时会变成一个真正的陷阱。假如我们想要去掉一个字符串里的所有元音字母,我们可能会写出如下的query:
IEnumerable<char> query = "How are you, friend.";
query = query.Where(c => c != 'a'); query = query.Where(c => c != 'e'); query = query.Where(c => c != 'i'); query = query.Where(c => c != 'o'); query = query.Where(c => c != 'u');
foreach (char c in query) Console.Write(c); //Hw r y, frnd.
尽管程序结果正确,但我们都能看出,如此写出来的程序不够优雅。所以我们会自然而然的想到使用foreach循环来重构上面这段程序:
IEnumerable<char> query = "How are you, friend."; foreach(char vowel in "aeiou") query = query.Where(c => c != vowel); foreach (char c in query) Console.Write(c); //How are yo, friend.
结果中只有字母u被过滤了,咋一看,有没有吃一惊呢!但只要仔细一想就能知道原因:因为vowel定义在循环之外,所以每个lambda表达式都捕获了同一变量。当我们的query执行时,vowel的值是什么呢?不正是被过滤的字母u嘛。要解决这个问题,我们只需把循环变量赋值给一个内部变量即可,如下面的temp变量作用域只是当前的lambda表达式。
IEnumerable<char> query = "How are you, friend."; foreach (char vowel in "aeiou") { char temp = vowel; query = query.Where(c => c != temp); } foreach (char c in query) Console.Write(c); //Hw r y, frnd.
仔细想想也没什么问题。因为LINQ查询的时候,生成的方法,每个方法都不是直接执行,而是都指向的是vowel的引用,而VOWEL引用在遍历结束后的值为u,实际上是执行了多次的query = query.Where(c => c != vowel);
而写成内部临时变量,则每个query指向的是不同的temp。