This question is based on the answer given by one of my favourite posters Mehrdad Afshari in this question about closure.
I am having a hard time understand why C# generates the code the way it does......
Here is the code in question
static void Main(string[] args)
{
List<string> list = new List<string> { "hello world", "TED", "goodbye world" };
IEnumerable<string> filteredList1 = list;
IEnumerable<string> filteredList2 = list;
var keywords = new[] { "hello", "world" };
foreach (var keyword in keywords)
{
//Will execute the following
//filteredList1 = filteredList1.Where(item => item.Contains("hello")).Where(item => item.Contains("world"));;
string value = keyword;
filteredList1 = filteredList1.Where(item => item.Contains(value));
//Will execute the following
//filteredList2 = filteredList2.Where(item => item.Contains("world"))
filteredList2 = filteredList2.Where(item => item.Contains(keyword));
}
Console.WriteLine("===================================================");
Console.WriteLine("LIST 1");
foreach (var s in filteredList1) // closure is called here
Console.WriteLine(s);
Console.WriteLine("===================================================");
Console.WriteLine("LIST 2");
foreach (var s in filteredList2) // closure is called here
Console.WriteLine(s);
Console.WriteLine("===================================================");
}
}
Gives the following output
===============
LIST 1
hello world
===============
LIST 2
hello world
goodbye world
===============
My problem is that I don't understand why filteredList2 list not generating the same code as filteredList1. It seems more sensible to that with each iteration of the foreach (var keyword in keywords) should just append another .Where(item => item.Contains(keyword)) and pass in a copy keyword current value.
Why does it not do this?
EDIT : OK maybe I was not clear. I understand When and how the closure generated, however I don't understand WHY it is done like this. Surely it makes sense hat if a compiler detects a loop variable is being used then why can it not generate a temp variable and ultimately end up in the same situation as filteredList1. Am I missing something here? May be there is some scenario where you would want to pass the same context to a lambda multiple times, but even then it always makes sense for the compiler to use local variable to store the value of loop variable when it is used as a context for a lambda.
To quote Jon Skeets definition of closure "To put it very simply, closures allow you to encapsulate some behaviour, pass it around like any other object, and still have access to the context in which they were first declared."
Surely you guys can see that c# closure over loop variable loses the context of the loop variable in which it was first set.
P.S. Please excuse the waffling I have a severe flu and trying to be concise is very difficult :-)