Этот вопрос основан на ответе одного из моих любимых постеров Мехрдада Афшари в этом вопросе о закрытии.
Мне трудно понять, почему C # генерирует код так, как он делает ......
Вот код вопроса
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("===================================================");
}
}
Дает следующий вывод
===============
LIST 1
hello world
===============
LIST 2
hello world
goodbye world
===============
Моя проблема в том, что я не понимаю, почему в списке FilterList2 не генерируется тот же код, что и в FilterList1.
Кажется более разумным, что с каждой итерацией foreach (ключевое слово var в ключевых словах) нужно просто добавить еще один .Where (item => item.Contains (ключевое слово)) и передать текущее значение ключевого слова copy.
Почему он этого не делает?
РЕДАКТИРОВАТЬ : ОК, может быть, я не был ясен. Я понимаю, когда и как возникло замыкание, однако я не понимаю, ПОЧЕМУ это делается так. Конечно, имеет смысл, если компилятор обнаруживает, что используется переменная цикла, тогда почему он не может сгенерировать временную переменную и в конечном итоге оказывается в той же ситуации, что и FilterList1. Я что-то здесь упускаю?
Может быть, есть сценарий, когда вы хотите передать один и тот же контекст в лямбду несколько раз, но даже тогда компилятору всегда имеет смысл использовать локальную переменную для хранения значения переменной цикла, когда он используется в качестве контекста для лямбда
Процитируем определение замыкания Джона Скита «Проще говоря, замыкания позволяют вам инкапсулировать некоторое поведение, передавать его, как любой другой объект, и по-прежнему иметь доступ к контексту, в котором они были впервые объявлены».
Конечно, вы, ребята, видите, что закрытие переменной c # над переменной цикла теряет контекст переменной цикла, в которой она была впервые установлена.
P.S. Прошу прощения, вафля, у меня сильный грипп, и попытка быть лаконичной очень трудна: -)