C # Закрытия, почему loopvariable захвачен ссылкой?

В этом примере я пытаюсь передать значение, но вместо этого передается ссылка.

for (int i = 0; i  new PhoneJobTest(i); t.Start(); } 

Это можно исправить следующим образом:

  for (int i = 0; i  new PhoneJobTest(jobNum); t.Start(); } 

Что здесь происходит? Почему исходный пример передает ссылку?

Ну, вот как работает C #. Выражение lambda в вашем выражении создает лексическое замыкание, в котором сохраняется единственная ссылка на i которая сохраняется даже после завершения цикла.

Чтобы исправить это, вы можете сделать то, что вы сделали.

Не стесняйтесь читать больше об этой конкретной проблеме по всему Интернету; моим выбором будет обсуждение Эрика Липперта.

Это проще понять, если вы посмотрите, что происходит, с точки зрения объема:

 for (int i = 0; i < 10; i++) { Thread t = new Thread(() => new PhoneJobTest(i); t.Start(); } 

В основном это означает нечто очень близкое:

 int i = 0; while (i < 10) { Thread t = new Thread(() => new PhoneJobTest(i); t.Start(); i++; } 

Когда вы используете выражение lambda, и оно использует переменную, объявленную за пределами lambda (в вашем случае, i ), компилятор создает что-то, называемое замыканием – временным classом, который «обертывает» переменную i и передает ее делегату генерируемый лямбдой.

Закрытие построено на том же уровне, что и переменная (i), поэтому в вашем случае:

 int i = 0; ClosureClass = new ClosureClass(ref i); // Defined here! (of course, not called this) while (i < 10) { Thread t = new Thread(() => new PhoneJobTest(i); t.Start(); i++; } 

Из-за этого каждый stream получает одно и то же ограничение.

Когда вы переработаете свой цикл для использования временного, вместо этого на этом уровне создается замыкание:

 for (int i = 0; i < 10; i++) { int jobNum = i; ClosureClass = new ClosureClass(ref jobNum); // Defined here! Thread t = new Thread(() => new PhoneJobTest(jobNum); t.Start(); } 

Теперь каждый stream получает свой собственный экземпляр, и все работает правильно.

Короткий ответ: закрытие. Длинный ответ здесь (среди других мест): Различное поведение при запуске streamа: ParameterizedThreadStart против анонимного делегата. Почему это имеет значение?

Вы определенно хотите прочитать «Заключение над переменной цикла, считающееся вредным» Эриком Липпертом:

  • Часть 1
  • Часть 2

Вкратце: поведение, которое вы видите, именно так работает C #.

Это происходит из-за того, как C # передает параметры lambda. Он переносит доступ переменной к classу, который создается во время компиляции, и предоставляет его как поле для тела лямбды.

При использовании анонимного делегата или lambda-выражения создается замыкание, поэтому ссылки на внешние переменные могут быть указаны. Когда создается замыкание, переменные стека (значения) повышаются до кучи.

Один из способов избежать этого – запустить stream с делегатом ParameterizedThreadStart. НАПРИМЕР:

  static void Main() { for (int i = 0; i < 10; i++) { bool flag = false; var parameterizedThread = new Thread(ParameterizedDisplayIt); parameterizedThread.Start(flag); flag = true; } Console.ReadKey(); } private static void ParameterizedDisplayIt(object flag) { Console.WriteLine("Param:{0}", flag); } 

Совсем случайно я столкнулся с этой концепцией только вчера: Link

  • Что является результатом NULL + int?
  • Какова цель (-ы) встроенного?
  • Как преобразовать объект JSON в пользовательский объект C #?
  • Заказ использования пространства имен std; и включает?
  • Ограничения оператора C # - почему?
  • Я получаю «Определение отчета для отчета« xxxx.rdlc »не указано» в моем отчете RDLC
  • Является ли int ссылочным типом или типом значения?
  • Path.Combine для URL-адресов?
  • (Частично), специализирующийся на несимметричном шаблоне параметра зависимого типа
  • Каков наилучший способ сделать обратный цикл «for» с индексом без знака?
  • Обработка проверки ModelState в веб-интерфейсе ASP.NET
  • Давайте будем гением компьютера.