以下代码有何作用?
class Base { }
class Derived : Base { }
class Test
{
void Foo(List<Base> list)
{
foreach (Derived obj in list)
{
// ...
}
}
}
我没想到它甚至会编译,但确实如此。
答案 0 :(得分:10)
您正在观察的行为是根据 8.8.4 C#语言规范的foreach语句部分。本节定义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)
{
...
}
决定让编译器引入演员。
答案 1 :(得分:7)
绝对没有,但它以非常低效的方式做到了。
操作顺序如下:
修改强>
根据您的编辑,如果传递给InvalidCastException
的列表中的任何元素实际上不是Foo
对象,则会冒Derived
的风险。
<强> EDIT2 强>
为什么编译?因为foreach
涉及对列表中的每个项隐式转换为Object
,所以另一个显式转换为foreach
块中的指定类型,在本例中为Derived
答案 2 :(得分:3)
foreach
包含演员。考虑它可以与非模板化的对象枚举一起使用。从Base
到Derived
的转换是有效的,因此代码有效,但可能在运行时抛出异常。
答案 3 :(得分:0)
不确定为什么不期望这个编译。考虑一下这在功能上是等价的:
{
List<Base> list = new List<Base>(); // creates new, empty list of Base
foreach (Derived obj in list)
{
// ...
}
}
这是否能让您更清楚地了解代码中的内容?
编辑重新修改版本。
如果您的列表包含任何不是Derived实例的内容,它将抛出InvalidCastException。派生'是'基础所以编译这个仍然没有问题。
答案 4 :(得分:0)
相当于:
Enumerator<Base> enumerator = new List<Base>().GetEnumerator();
while (enumerator.MoveNext())
{
Derived obj = (Derived) enumerator.Current;
// ...
}
由于您的列表为空,因此内部块不会执行。
如果它包含元素,那么如果任何元素不属于InvalidCastException
类型,则存在Derived
的风险。
答案 5 :(得分:0)
从技术上讲,Randolpho是正确的:你的代码什么都不做。但我认为你正处于另一个不同的阶段。
将您的列表项强制转换为Derived
,除了Derived
类中定义的属性外,还可以访问Base
类中定义的属性。但是,当您访问这些属性时,如果列表中的类型不是Derived
的项目,则会出现错误。
除了其他一些需求外,最好将列表定义为List<Derived>
。
答案 6 :(得分:0)
与
相同list.Cast<Derived>();
它只是输入铸造。在某些情况下,你可能会有错误