Выполняет ли foreach() итерацию по ссылке?



рассмотрим следующий пример:



List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}


является ли " obj " ссылкой на соответствующий объект в списке, чтобы при изменении свойства изменение сохранялось в экземпляре объекта, когда-то построенном где-то?

698   10  

10 ответов:

Да obj - Это ссылка на текущий объект в коллекцию (при условии MyClass на самом деле класс). Если вы изменяете какие-либо свойства через ссылку, вы меняете объект, как и следовало ожидать.

имейте в виду, однако, что вы не можете изменить переменную obj сам по себе, как это переменная итерации. Вы получите ошибку компиляции, если вы попытаетесь. Это означает, что вы не можете обнулить его, и если вы повторяете типы значений, вы не можете изменять какие-либо члены, как это было бы изменение значения.

состояние спецификации языка C# (8.8.4)

"переменная итерации соответствует локальная переменная только для чтения С a область, которая распространяется на встроенную заявление."

Да, пока вы не измените универсальный тип из списка на IEnumerable..

вы задали 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(); делает глубокую копию списка или возвращает тот же экземпляр.

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

Comments

    Ничего не найдено.