Выполняется ли foreach() по ссылке?
Учтите это:
List<MyClass> obj_list = get_the_list();
foreach( MyClass obj in obj_list )
{
obj.property = 42;
}
Является ли obj
ссылкой на соответствующий объект в списке, чтобы при изменении свойства это изменение сохранялось в экземпляре объекта, когда-то построенном?
Ответы
Ответ 1
Да, obj
- ссылка на текущий объект в коллекции (при условии, что MyClass
- фактически класс). Если вы измените какие-либо свойства с помощью ссылки, вы меняете объект, как и ожидали.
Помните, однако, что вы не можете изменить переменную obj
, так как она является переменной итерации. Если вы попробуете, вы получите ошибку компиляции. Это означает, что вы не можете его обнулить, и если вы повторяете типы значений, вы не можете изменять какие-либо элементы, поскольку это изменит значение.
Спецификация языка С# заявляет (8.8.4)
"Итерационная переменная соответствует локальная переменная, доступная только для чтения, с объем, который распространяется на встроенный утверждение".
Ответ 2
Да, пока вы не измените общий тип из списка в IEnumerable..
Ответ 3
Вы задали здесь 2 разных вопроса, давайте их в порядок.
Проводится ли цикл foreach по ссылке?
Если вы имеете в виду в том же смысле, что и цикл С++ для ссылки, то нет. С# не имеет ссылок на локальные переменные в том же смысле, что и С++, и, следовательно, не поддерживает этот тип итераций.
Будет ли сохраняться изменение
Предполагая, что MyClass является ссылочным типом, ответ да. Класс является ссылочным типом в .Net, и поэтому переменная итерации является ссылкой на одну переменную, а не на копию. Это не относится к типу значения.
Ответ 4
Хорошо, мне случилось, что мои изменения не обновлялись в цикле foreach, когда я повторялся через коллекцию var:
var players = this.GetAllPlayers();
foreach (Player player in players)
{
player.Position = 1;
}
Когда я изменил var на List, он начал работать.
Ответ 5
В этом случае вы можете использовать (используя List<T>
), но если вы будете выполнять итерацию по общему IEnumerable<T>
, тогда он будет зависеть от его реализации.
Если бы это было все равно List<T>
или T[]
, то все работало бы так, как ожидалось.
Большая магия возникает, когда вы работаете с IEnumerable<T>
, который был создан с использованием yield. В этом случае вы не можете больше изменять свойства T
в рамках итерации и ожидать, что они будут присутствовать, если вы снова повторите те же самые IEnumerable<T>
.
Ответ 6
это верно, если это не структура.
Ответ 7
Может быть, вам интересно полагать, что в версии С# 7.3 можно изменять значения по ссылке при условии, что свойство Current перечислителя возвращает ссылочный тип. Следующее будет действительным (дословная копия из документов MS):
Span<int> storage = stackalloc int[10];
int num = 0;
foreach (ref int item in storage)
{
item = num++;
}
Подробнее об этой новой функции читайте на
С# оператор foreach | Документы Microsoft.
Ответ 8
Ну, не понимая, что вы подразумеваете под "Итерацией по ссылке", я не могу ответить конкретно "да" или "нет", но могу сказать, что то, что происходит под поверхностью, состоит в том, что инфраструктура .net строит "счетчик" "класс для каждого клиентского кода вызывает вызов foreach в течение жизни foreach, который поддерживает ссылочный указатель на итерацию цикла, и каждый раз, когда ваш foreach выполняет итерацию, ir" поставляет "один элемент и" увеличивает "указатель или ссылку в перечислителе на следующий элемент...
Это происходит независимо от того, являются ли элементы в коллекции, которую вы итерируете, типами значений или ссылочными типами.
Ответ 9
obj - ссылка на элемент внутри Списка, поэтому, если вы измените его значение, оно будет сохраняться. Теперь, о чем вам следует беспокоиться, является ли get_the_list(); делает глубокую копию списка или возвращает тот же экземпляр.
Ответ 10
Да, это также почему вы не можете изменить перечислимый объект в контексте инструкции foreach.