IEnumerable<IList<object>> 上的 C# foreach 编译但不应该
C# foreach on IEnumerable<IList<object>> compiles but shouldn't
我有以下代码:
IEnumerable<IList<MyClass>> myData = //...getMyData
foreach (MyClass o in myData)
{
// do something
}
它编译、运行,显然我得到一个 System.InvalidCastException
。
为什么编译器不抱怨? MyClass
是一个简单的 bean,没有扩展。
编辑 1:
正如 David 所建议的那样,将类型从 IList
切换到 List
编译器会抱怨
编辑 2:
我了解该行为是在 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 中指定的,以便更深入。
当 foreach
被编译时,它遵循一种模式而不是特定类型(就像 LINQ 和 await
所做的那样)。
foreach
不是在寻找 IEnumerable
或 IEnumerable<T>
,而是在寻找具有 GetEnumerator()
方法(IList<T>
有)的类型。并且外部列表中的对象可以是从 MyClass
派生并实现 IList<T>
).
的类型
即。编译器进行轻量级 "matches the pattern" 检查而不是完整检查。
请参阅 C#5 语言规范的第 8.8.3 节,其中详细介绍了这一点(你会看到我已经相当简化了上面的内容:甚至 IEnumerator
都没有被检查,只是有一个 MoveNext()
方法和一个 Current
属性).
IList<MyClass>
可转换为 MyClass
.
但是如果你实际上 运行 它带有一个非空的枚举,
IEnumerable<IList<MyClass>> myData = new IList<MyClass>[1] { new List<MyClass>() {new MyClass()}};
您收到此错误:
Unable to cast object of type 'System.Collections.Generic.List`1[MyClass]' to type 'MyClass'.
这符合规范:
Section 8.8.4 The foreach statement
...
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
的显式转换(尽管它会在运行时失败),所以没有错误出品了。
Section 6.2.4 Explicit reference conversions
The explicit reference conversions are:
- 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.
...
好吧,因为 IList<MyClass>
是一个接口,所以理论上你可以有一个 class 实现了该接口并且派生自 MyClass
。
如果将其更改为 IEnumerable<List<MyClass>>
,它将无法编译。
无论如何,至少我收到了可疑转换的警告,因为解决方案中没有继承自 IList<MyClass>
和 MyClass
的 class。
假设 MyClass
没有实现 IList<MyClass>
,可能会有一个 MyClass
的派生类型实现了 IList<MyClass>
,那么你的循环将是有效的.
即
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
}
我有以下代码:
IEnumerable<IList<MyClass>> myData = //...getMyData
foreach (MyClass o in myData)
{
// do something
}
它编译、运行,显然我得到一个 System.InvalidCastException
。
为什么编译器不抱怨? MyClass
是一个简单的 bean,没有扩展。
编辑 1:
正如 David 所建议的那样,将类型从 IList
切换到 List
编译器会抱怨
编辑 2:
我了解该行为是在 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 中指定的,以便更深入。
当 foreach
被编译时,它遵循一种模式而不是特定类型(就像 LINQ 和 await
所做的那样)。
foreach
不是在寻找 IEnumerable
或 IEnumerable<T>
,而是在寻找具有 GetEnumerator()
方法(IList<T>
有)的类型。并且外部列表中的对象可以是从 MyClass
派生并实现 IList<T>
).
即。编译器进行轻量级 "matches the pattern" 检查而不是完整检查。
请参阅 C#5 语言规范的第 8.8.3 节,其中详细介绍了这一点(你会看到我已经相当简化了上面的内容:甚至 IEnumerator
都没有被检查,只是有一个 MoveNext()
方法和一个 Current
属性).
IList<MyClass>
可转换为 MyClass
.
但是如果你实际上 运行 它带有一个非空的枚举,
IEnumerable<IList<MyClass>> myData = new IList<MyClass>[1] { new List<MyClass>() {new MyClass()}};
您收到此错误:
Unable to cast object of type 'System.Collections.Generic.List`1[MyClass]' to type 'MyClass'.
这符合规范:
Section 8.8.4 The foreach statement
... 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
的显式转换(尽管它会在运行时失败),所以没有错误出品了。
Section 6.2.4 Explicit reference conversions
The explicit reference conversions are:
- 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.
...
好吧,因为 IList<MyClass>
是一个接口,所以理论上你可以有一个 class 实现了该接口并且派生自 MyClass
。
如果将其更改为 IEnumerable<List<MyClass>>
,它将无法编译。
无论如何,至少我收到了可疑转换的警告,因为解决方案中没有继承自 IList<MyClass>
和 MyClass
的 class。
假设 MyClass
没有实现 IList<MyClass>
,可能会有一个 MyClass
的派生类型实现了 IList<MyClass>
,那么你的循环将是有效的.
即
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
}