#c# #.net #syntax #foreach #syntactic-sugar
#c# #.net #синтаксис #foreach #синтаксический сахар
Вопрос:
Компилятор компилирует foreach
цикл во что-то вроде for
цикла, когда foreach
используется с массивом. И компилятор компилирует foreach
цикл во что-то вроде while
цикла, когда foreach
используется с IEnumerable
или IEnumerable<T>
. Значит ли это, что foreach
это чисто syntactic sugar
? Или в этом есть что-то сложное?
Знает ли CLR об foreach
? Есть ли что-нибудь специально разработанное для foreach
в коде MSIL?
Ответ №1:
Это чисто синтаксический сахар в том смысле, что вы могли бы получить такое же поведение без него, да. Многие другие вещи такие же … for
, while
и т.д… Неверно процитирую Архимеда: «Дайте мне if
и goto
, и я перенесу код …»
Нет, в CLR нет никакого понятия foreach
.
Ответ №2:
Это синтаксический сахар. Однако обратите внимание, что foreach работает путем вызова GetEnumerator(), затем MoveNext() до тех пор, пока не будет возвращен ни один другой элемент, а затем всегда вызывает Dispose() для перечислителя, который он ранее получил. Если вы хотите сделать это таким же образом, не забудьте, что Dispose() !
Кроме того, среда CLR выполняет некоторые трюки, связанные с получением перечислителя. Смотрите, например, здесь и здесь.
Ответ №3:
foreach
внутренне это просто while
цикл, который вызывает методы в IEnumerator
.
Ответ №4:
Да, это чистый сахар. Следующий код
var MyList = new List<int>() { 10 , 20 , 30 , 40, 50} ;
foreach(int i in MyList)
{
Console.WriteLine(i);
}
переводится в компиляторе как:
Ienumrator<int> rator = MyList.GetEnumrator();
try
{
while(rator.MoveNext())
{
int i = rator.Current;
Console.WriteLine(i);
}
}
finally
{
rator.Dispose()
}
Ответ №5:
Это не просто синтаксический сахар, поскольку элементы в цикле foreach неизменяемы. Причина этого, как любезно указал Дэниел, заключается в том, что большинство коллекций будут использовать перечислитель в foreach, и именно перечислитель имеет ограничение, не позволяющее вам обновлять содержимое списка во время его перечисления.
т. е.
Foreach(String s in List<string>)
{
s = "f"; //this will throw an error.
}
Комментарии:
1. интересно, тогда как мы можем имитировать такого рода поведение, используя цикл for или while?
2. теперь, когда я думаю об этом снова, это кажется неправильным. это всего лишь искусственное правило, которое помогает уменьшить недопонимание.
3. Это синтаксический сахар, и это не совсем определение неизменяемого. Сама переменная не может быть изменена, но объект, на который она ссылается, может быть изменен любым способом, поддерживаемым объектом (хотя, конечно, не в случае уже неизменяемого типа).
4. -1: Это выдаст ошибку, потому что foreach использует перечислитель из List. Этот перечислитель выдает ошибку, если вы продолжаете использовать его после изменения списка. В foreach нет ничего особенного.
5. @daniel вы правы в том, что на самом деле ошибку выдает перечислитель, но в ответ на вопрос OP я надеялся показать, что существует фундаментальное отличие в использовании foreach от обычного цикла for, который будет вызывать ошибки в зависимости от того, как вы его используете.