Ответ 1
Foreach может вызывать распределения, но, по крайней мере, в более новых версиях .NET и Mono, это не так, если вы имеете дело с конкретными типами или массивами типа System.Collections.Generic
. Старые версии этих компиляторов (например, версия Mono, используемая Unity3D до 5.5) всегда генерируют распределения.
Компилятор С# использует duck typing для поиска метода GetEnumerator()
и использует это, если это возможно. Большинство методов GetEnumerator()
в типах System.Collection.Generic
имеют методы GetEnumerator(), которые возвращают structs, и массивы обрабатываются специально. Если ваш метод GetEnumerator()
не выделяется, вы обычно можете избежать выделения.
Однако вы всегда будете получать выделение, если имеете дело с одним из интерфейсов IEnumerable
, IEnumerable<T>
, IList
или IList<T>
. Даже если ваш реализующий класс возвращает структуру, структура будет помещена в бокс и добавлена к IEnumerator
или IEnumerator<T>
, которая требует выделения.
ПРИМЕЧАНИЕ. Поскольку Unity 5.5 обновлен до С# 6, я не знаю текущей версии компилятора, которая все еще имеет это второе выделение.
Есть второе выделение, которое немного сложнее понять. Возьмите этот цикл foreach:
List<int> valueList = new List<int>() { 1, 2 };
foreach (int value in valueList) {
// do something with value
}
До С# 5.0 он расширяется до примерно такого (с некоторыми небольшими отличиями):
List<int>.Enumerator enumerator = valueList.GetEnumerator();
try {
while (enumerator.MoveNext()) {
int value = enumerator.Current;
// do something with value
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
В то время как List<int>.Enumerator
является структурой и не нужно выделять в куче, cast enumerator as System.IDisposable
блокирует структуру, которая является распределением. Спецификация изменена с помощью С# 5.0, запрещающая выделение, но .NET нарушил спецификацию и оптимизировал выделение раньше.
Эти распределения крайне незначительны. Обратите внимание, что распределение сильно отличается от утечки памяти, и с сборкой мусора вам вообще не нужно беспокоиться об этом. Тем не менее, есть некоторые сценарии, когда вы заботитесь даже об этих распределениях. Я работаю Unity3D и до 5.5, мы не могли иметь никаких распределений в операциях, которые бывают в каждом игровом фрейме, потому что, когда сборщик мусора работает, вы получаете заметную неудачу.
Обратите внимание, что петли foreach на массивах обрабатываются специально и не нужно вызывать Dispose. Насколько я могу судить, foreach никогда не выделял при переходе по массивам.