foreach (Производный объект в новом Списке <Base>()) - PullRequest
5 голосов
/ 12 октября 2010

Что делает следующий код?

class Base { }
class Derived : Base { }
class Test
{
    void Foo(List<Base> list)
    {
        foreach (Derived obj in list)
        {
            // ...
        }
    }
}

Я не ожидал, что он даже скомпилируется, но это так.

Ответы [ 7 ]

10 голосов
/ 12 октября 2010

Поведение, которое вы наблюдаете, соответствует разделу 8.8.4 оператора foreach спецификации языка C #.Этот раздел определяет семантику оператора foreach следующим образом:

[...] Приведенные выше шаги в случае успеха однозначно создают тип коллекции C, тип перечислителя E итип элемента T.Оператор foreach вида

foreach (V v in x) embedded-statement

затем расширяется до:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        V v;
        while (e.MoveNext()) {
            // here the current item will be casted                
            v = (V)(T)e.Current;
            embedded-statement
        }
    }
    finally {
        // Dispose e
    }
}

Причина, по которой компилятор вставляет это явное приведение, является исторической.В C # 1.0 не было обобщений, поэтому, чтобы разрешить простой код, подобный этому

ArrayList list = new ArrayList();
list.Add(1);
foreach (int i in list)
{
   ...
}

, было решено позволить компилятору представить приведение.

7 голосов
/ 12 октября 2010

Абсолютно ничего, но это делает это очень неэффективно.

Порядок операций следующий:

  1. Создание нового списка с нулевыми элементами
  2. Итерация по вновь созданному списку, в котором нет элементов
  3. Не приведение базового объекта к производному объекту, поскольку в списке нет элементов.Если в списке есть какие-либо элементы, этот шаг приведет к исключению времени выполнения.
  4. Не выполнять код в блоке foreach, поскольку список содержит ноль элементов.

Редактировать
Исходя из ваших правок, вы рискуете InvalidCastException, если какой-либо элемент списка, переданный Foo, фактически не будет Derived объект.

Edit2
Почему он компилируется?Поскольку foreach включает неявное приведение к Object для каждого элемента в списке, тогда другое явное приведение к указанному типу в блоке foreach, в данном случае Derived

3 голосов
/ 12 октября 2010

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

0 голосов
/ 12 октября 2010

так же, как

list.Cast<Derived>();

это просто набросок типа. в некоторых случаях вы можете получить ошибку

0 голосов
/ 12 октября 2010

Технически, Рандольфо прав: твой код ничего не делает. Но я думаю, что вы попали в другую точку.

Преобразование элементов списка в Derived даст вам доступ к свойствам, определенным в классе Derived, в дополнение к свойствам, определенным в классе Base. Однако вы получите ошибки, если у вас есть элементы не типа Derived в списке при доступе к этим свойствам.

Если исключить некоторые другие потребности, вам лучше иметь список, определенный как List<Derived>.

0 голосов
/ 12 октября 2010

Это эквивалентно:

Enumerator<Base> enumerator = new List<Base>().GetEnumerator();
while (enumerator.MoveNext())
{
    Derived obj = (Derived) enumerator.Current;
    // ...
}

Поскольку ваш список пуст, внутренний блок не выполняется.

Если бы он содержал элементы, то был бы риск InvalidCastException, если какой-либо из элементов не был бы типа Derived.

0 голосов
/ 12 октября 2010

Не уверен, почему бы не ожидать, что это скомпилировать.Подумайте об этом, что функционально эквивалентно:

{
  List<Base> list = new List<Base>();  // creates new, empty list of Base
  foreach (Derived obj in list)
  {
    // ...
  }
}

Делает ли это более понятным то, что происходит в вашем коде?

ПРАВКА пересмотренной версии.

Теперь он выдастInvalidCastException, если ваш список содержит все, что не является экземпляром Derived.Derived 'is' Base ', поэтому по-прежнему нет проблем с компиляцией.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...