foreach(新列表中的派生对象<base />())

时间:2010-10-12 16:57:16

标签: c# inheritance syntax foreach polymorphism

以下代码有何作用?

class Base { }
class Derived : Base { }
class Test
{
    void Foo(List<Base> list)
    {
        foreach (Derived obj in list)
        {
            // ...
        }
    }
}

我没想到它甚至会编译,但确实如此。

7 个答案:

答案 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)

绝对没有,但它以非常低效的方式做到了。

操作顺序如下:

  1. 实例化一个包含零项的新列表
  2. 迭代新实例化的列表,其中包含零项
  3. 未将Base对象强制转换为Derived对象,因为该列表的项目为零。如果列表包含任何项,则此步骤将导致运行时异常。
  4. 不执行foreach块中的代码,因为列表中没有项目。
  5. 修改
    根据您的编辑,如果传递给InvalidCastException的列表中的任何元素实际上不是Foo对象,则会冒Derived的风险。

    <强> EDIT2
    为什么编译?因为foreach涉及对列表中的每个项隐式转换为Object,所以另一个显式转换为foreach块中的指定类型,在本例中为Derived

答案 2 :(得分:3)

foreach包含演员。考虑它可以与非模板化的对象枚举一起使用。从BaseDerived的转换是有效的,因此代码有效,但可能在运行时抛出异常。

答案 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>();

它只是输入铸造。在某些情况下,你可能会有错误