Ответ 1
Это связано с тем, что foreach
не выполняет проверку времени компиляции в вашем конкретном случае. Если вы создали рабочий код, вы получите a InvalidCastException
во время выполнения.
using System.Collections.Generic;
public class Test
{
internal class Program
{
public static void Main()
{
var item = new Foo();
var inner = new List<Foo>();
var outer = new List<List<Foo>>();
inner.Add(item);
outer.Add(inner);
IEnumerable<IEnumerable<Foo>> data = outer;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
}
Выполнение foreach (Foo foo in data)
эквивалентно вызову
IEnumerator enumerator = ((IEnumerable)data).GetEnumerator();
Foo foo; //declared here in C# 4 and older
while(enumerator.MoveNext())
{
//Foo foo; //declared here in C# 5 and newer
foo = (Foo)enumerator.Current; //Here is the run time error in your code.
//The code inside the foreach loop.
{
foo.Bar();
}
}
Итак, вы видите, что все равно, какой тип вы передали, если вызов foo = (Foo)enumerator.Current;
преуспевает.
Причина, по которой он не выбрасывает ошибки времени компиляции, IEnumerable<T>
covariant. Это означает, что мне разрешено передавать любой класс, основанный на Foo
или больше, полученный из Foo
. Поэтому, если я могу потенциально сделать 2-й класс, который наследует от Foo
, который также поддерживает IEnumerable<Foo>
и содержит мой список, вместо этого это приведет к сбою приведения.
//This code compiles fine in .NET 4.5 and runs without throwing any errors.
internal class Program
{
public static void Main()
{
var item = new Baz();
var inner = new List<Baz>();
inner.Add(item);
IEnumerable<IEnumerable<Foo>> data = inner;
foreach (Foo foo in data)
{
foo.Bar();
}
}
}
public class Foo
{
public void Bar()
{
}
}
public class Baz : Foo, IEnumerable<Foo>
{
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<Foo> IEnumerable<Foo>.GetEnumerator()
{
throw new NotImplementedException();
}
}
Однако, если вы помечаете Foo
как sealed
, теперь компилятор знает, что не может существовать больше производных классов, а затем будет вызывать ошибку компилятора