C # Закрытия, почему loopvariable захвачен ссылкой?
В этом примере я пытаюсь передать значение, но вместо этого передается ссылка.
for (int i = 0; i new PhoneJobTest(i); t.Start(); }
Это можно исправить следующим образом:
for (int i = 0; i new PhoneJobTest(jobNum); t.Start(); }
Что здесь происходит? Почему исходный пример передает ссылку?
- Есть ли способ получить предупреждение о неиспользуемых функциях?
- log2 не найден в моей math.h?
- Удаление всех неглавных символов с помощью регулярных выражений (регулярные выражения в шаблоне регулярных выражений C #)
- Возвращаемый массив в функции
- Сравнение двойных значений в C #
- Обработка исключений - это хороший способ?
- Как преобразовать массив байтов в шестнадцатеричную строку и наоборот?
- Определите, является ли строка C допустимым int в C
- Можно ли рассматривать 2D-массив как непрерывный массив 1D?
- Преобразовать объект в строку JSON в C #
- Будет ли использование «var» влиять на производительность?
- Добавление полосы прокрутки в элемент управления MS Chart C #
- Как я могу выделить память и вернуть ее (через указательный параметр) в вызывающую функцию?
Ну, вот как работает 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