Выполняет ли foreach() итерацию по ссылке?
рассмотрим следующий пример:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
является ли " obj " ссылкой на соответствующий объект в списке, чтобы при изменении свойства изменение сохранялось в экземпляре объекта, когда-то построенном где-то?
10 ответов:
Да
obj- Это ссылка на текущий объект в коллекцию (при условииMyClassна самом деле класс). Если вы изменяете какие-либо свойства через ссылку, вы меняете объект, как и следовало ожидать.имейте в виду, однако, что вы не можете изменить переменную
objсам по себе, как это переменная итерации. Вы получите ошибку компиляции, если вы попытаетесь. Это означает, что вы не можете обнулить его, и если вы повторяете типы значений, вы не можете изменять какие-либо члены, как это было бы изменение значения.состояние спецификации языка C# (8.8.4)
"переменная итерации соответствует локальная переменная только для чтения С a область, которая распространяется на встроенную заявление."
вы задали 2 разных вопроса здесь, давайте рассмотрим их по порядку.
повторяется ли цикл foreach по ссылке?
Если вы имеете в виду в том же смысле, как C++ цикл for по ссылке, то нет. C# не имеет ссылок на локальные переменные в том же смысле, что и C++, и, следовательно, не поддерживает этот тип итерации.
будут ли изменения сохраняться
предполагая, что MyClass является ссылочным типом, ответ да. Класс-это ссылочный тип в .Net и, следовательно, переменная итерации является ссылкой на одну переменную, а не копией. Это было бы неверно для типа значения.
Ну, это случилось со мной, что мои изменения не были обновлены в цикле foreach, когда я перебирал коллекцию var:
var players = this.GetAllPlayers(); foreach (Player player in players) { player.Position = 1; }когда я изменил var на список, он начал работать.
вы можете в этом случае (используя
List<T>) но если бы вы должны были перебирать общийIEnumerable<T>тогда он становится зависимым от его реализации.если бы это было еще
List<T>илиT[]например, все будет работать, как ожидалось. Большой gotcha приходит, когда вы работаете сIEnumerable<T>Это было построено с использованием yield. В этом случае, вы больше не можете изменять свойстваTв течение итерации и ожидать, что они будут присутствовать, если вы повторяете то же самоеIEnumerable<T>снова.
Ну, не понимая точно, что вы подразумеваете под "итерацией по ссылке", я не могу ответить конкретно да или нет, но я могу сказать, что то, что происходит под поверхностью, заключается в том, что .net framework создает класс "перечислитель" для каждого вызова клиентского кода foreach, для жизни foreach, который поддерживает указатель ссылки в коллекцию, которая повторяется, и каждый раз, когда ваш foreach повторяется, ir "доставляет" один элемент и "увеличивает" указатель или ссылку в перечислитель к следующему пункту...
Это происходит независимо от того, являются ли элементы в коллекции, которые вы повторяете, типами значений или ссылочными типами.
может быть, вам интересно опереться на то, что по версии C# 7.3 можно изменить значения по ссылке при условии, что перечислитель настоящее свойство возвращает ссылочный тип. Следующее будет действительным (дословная копия из MS docs):
Span<int> storage = stackalloc int[10]; int num = 0; foreach (ref int item in storage) { item = num++; }Узнайте больше об этой новой функции на оператор c# foreach / Microsoft Docs.
obj-это ссылка на элемент внутри списка, поэтому при изменении его значения он будет сохраняться. теперь вы должны быть обеспокоены тем, является ли get_the_list(); делает глубокую копию списка или возвращает тот же экземпляр.
Comments