Является ли foreach чисто “синтаксическим сахаром”?

#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, который будет вызывать ошибки в зависимости от того, как вы его используете.