C#for IEnumerable< IList< object>编译但不应该

C#for IEnumerable< IList< object>编译但不应该

问题描述:

我有以下代码:

IEnumerable<IList<MyClass>> myData = //...getMyData

foreach (MyClass o in myData)
{
    // do something
}

它编译,运行,显然我得到了System.InvalidCastException.
为什么编译器不抱怨? MyClass是一个简单的bean,没有扩展名.

It compiles, runs and obviously I get an System.InvalidCastException.
Why does the compiler not complain? MyClass is a simple bean, no extensions.


正如David所建议的,将类型从IList切换为List时,编译器抱怨

Edit 1:
As suggested by David switching the type from IList to List the compiler complains


我了解该行为是 C#语言定义.但是,我不明白为什么允许这样的转换/转换,因为在运行时我总是会收到InvalidCastException.我在以下位置打开了为了更深入.

Edit 2:
I've understood that the behaviour is as specified in the C# Language definition. However, I don't understand why such a cast/conversion is allowed, since at runtime I always get an InvalidCastException. I opened this in order to go deeper.

IList<MyClass> 可转换为MyClass.

但是,如果您实际上使用非空枚举来运行它,

But if you actually run it with a non-empty enumerable,

IEnumerable<IList<MyClass>> myData = new IList<MyClass>[1] { new List<MyClass>() {new MyClass()}};

您收到此错误:

无法转换类型为'System.Collections.Generic.List`1 [MyClass]'的对象来键入'MyClass'.

Unable to cast object of type 'System.Collections.Generic.List`1[MyClass]' to type 'MyClass'.

这符合规范:

8.8.4节foreach语句

Section 8.8.4 The foreach statement

... 如果没有从T(该元素进行显式转换(第6.2节) 类型)转换为V(foreach语句中的局部变量类型), 错误产生,因此不采取进一步措施.

... If there is not an explicit conversion (§6.2) from T (the element type) to V (the local-variable-type in the foreach statement), an error is produced and no further steps are taken.

...

IList<MyClass>MyClass的显式转换(尽管它将在运行时失败),因此不会产生任何错误.

There is an explicit conversion from IList<MyClass> to MyClass (though it will fail at runtime), so no error is produced.

第6.2.4节显式引用转换

Section 6.2.4 Explicit reference conversions

显式引用转换为:

  • 从对象和动态到任何其他引用类型.
  • 从任何类别类型S到任何类别类型T,只要S是T的基类.
  • 从任何类类型S到任何接口类型T,只要未密封S并且未实现T.
  • 从任何接口类型S到任何类类型T,只要未密封T或提供T实现S.
  • From object and dynamic to any other reference-type.
  • From any class-type S to any class-type T, provided S is a base class of T.
  • From any class-type S to any interface-type T, provided S is not sealed and provided S does not implement T.
  • From any interface-type S to any class-type T, provided T is not sealed or provided T implements S.

...