我有以下代码:
IEnumerable<IList<MyClass>> myData = //...getMyData
foreach (MyClass o in myData)
{
// do something
}
它编译,运行,显然我得到了System.InvalidCastException
。
为什么编译器不抱怨? MyClass
是一个简单的bean,没有扩展名。
编辑1:
根据David的建议,将类型从IList
切换为List
,编译器抱怨
编辑2:
我了解到,该行为与C# Language definition中指定的行为相同。但是,我不明白为什么允许这样的转换/转换,因为在运行时我总是会收到InvalidCastException。我打开this是为了更深入。
答案 0 :(得分:6)
好吧,因为IList<MyClass>
是一个接口,所以从理论上讲,您可以拥有一个实现该接口并从MyClass
派生的类。
如果将其更改为IEnumerable<List<MyClass>>
,它将无法编译。
无论如何,至少我收到可疑演员表的警告,因为解决方案中没有从IList<MyClass>
和MyClass
继承的类。
答案 1 :(得分:5)
编译foreach
时,它遵循的模式不是特定类型(就像LINQ和await
一样)。
foreach
不是在寻找IEnumerable
或IEnumerable<T>
,而是在寻找具有GetEnumerator()
方法的类型(IList<T>
是)。并且外部列表中的对象可以是从MyClass
派生并实现IList<T>
)的类型。
即。编译器会进行轻量级的“匹配模式”检查,而不是完全检查。
请参阅C#5语言规范的8.8.3节,其中对此进行了详细介绍(并且您会看到上面我已经做了一些简化:即使IEnumerator
都没有检查,只是有MoveNext()
方法和Current
属性)。
答案 2 :(得分:4)
IList<MyClass>
可转换为MyClass
。
但是如果您实际上使用非空枚举来运行它,
IEnumerable<IList<MyClass>> myData = new IList<MyClass>[1] { new List<MyClass>() {new MyClass()}};
您收到此错误:
无法转换类型为'System.Collections.Generic.List`1 [MyClass]'的对象来键入'MyClass'。
这符合规范:
8.8.4节foreach语句
... 如果没有从T(元素)进行显式转换(第6.2节) 类型)转换为V(foreach语句中的局部变量类型), 错误产生,因此不采取进一步措施。
...
是从IList<MyClass>
到MyClass
的显式转换(尽管它将在运行时失败),因此不会产生任何错误。
第6.2.4节显式引用转换
显式引用转换为:
- 从对象和动态到任何其他引用类型。
- 从任何类别类型S到任何类别类型T,只要S是T的基类。
- 从任何类类型S到任何接口类型T,只要未密封S并且未实现T。
- 从任何接口类型S到任何类类型T,只要未密封T或提供T实现S。
...
答案 3 :(得分:2)
在假设MyClass
未实现IList<MyClass>
的情况下,可能会有确实实现MyClass
的派生类型IList<MyClass>
,然后您的循环才有效。 / p>
即
class MyClass
{
}
class Derived : MyClass, IList<MyClass>
{
// ...
}
// ...
// Here IList<MyClass> is Derived, which is valid because Derived implements IList<MyClass>
IEnumerable<IList<MyClass>> myData = new []{new Derived()};
// Here MyClass is Derived, which is valid because Derived inherits from MyClass
foreach (MyClass o in myData)
{
// do something
}