代码之家  ›  专栏  ›  技术社区  ›  Stefan Monov

foreach(新列表中的派生对象)

  •  5
  • Stefan Monov  · 技术社区  · 15 年前

    以下代码的作用是什么?

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

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

    7 回复  |  直到 15 年前
        1
  •  10
  •   Dirk Vollmar    15 年前

    你所观察到的行为是根据 8.8.4 foreach声明 C语言规范。本节定义了 foreach 声明如下:

    […]如果成功,上述步骤将明确生成集合类型 C ,枚举器类型 E 和元素类型 T . 一 前额 表格的声明

    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)
    {
       ...
    }
    

    决定让编译器引入强制转换。

        2
  •  7
  •   Randolpho    15 年前

    绝对没有,但它是以一种非常低效的方式完成的。

    操作顺序如下:

    1. 用零项实例化新列表
    2. 迭代新实例化的列表,其中没有项
    3. 不将基对象强制转换为派生对象,因为列表中没有项。如果列表中有任何项,则此步骤将导致运行时异常。
    4. 不执行foreach块中的代码,因为列表中没有项。

    编辑
    根据您的编辑,您将面临 InvalidCastException 是否应将列表中的任何元素传递给 Foo 实际上不是 Derived 对象。

    编辑2
    为什么要编译?因为 foreach 包含一个隐式强制转换到 Object 对于列表中的每个项,然后将另一个显式强制转换为 前额 阻止,在这种情况下 衍生的

        3
  •  3
  •   Jon Hanna    15 年前

    foreach 包括铸件。考虑到它可以与对象的非模板化枚举一起使用。铸造自 Base Derived 有效,因此代码有效,但可能在运行时引发异常。

        4
  •  0
  •   Steve Townsend    15 年前

    不知道为什么不希望编译这个。考虑到这一点,其功能相当:

    {
      List<Base> list = new List<Base>();  // creates new, empty list of Base
      foreach (Derived obj in list)
      {
        // ...
      }
    }
    

    这是否使代码中发生的事情更加清楚?

    编辑重新修订的版本。

    现在,如果列表中包含任何不是派生实例的内容,它将抛出InvalidcastException。派生的“是”基,因此编译此基仍然没有问题。

        5
  •  0
  •   Brad    15 年前

    从技术上讲,Randolpho是正确的:您的代码什么也不做。但我认为你的观点不同。

    将列表项强制转换为 Derived 将允许您访问中定义的属性 衍生的 类中定义的属性之外, Base 班级。但是,如果有不属于类型的项,则会出现错误。 衍生的 在列表中访问这些属性时。

    除非有其他需要,你最好把清单定义为 List<Derived> .

        6
  •  0
  •   Bonshington    15 年前

    一样

    list.Cast<Derived>();
    

    只是打字而已。在某些情况下,你可能会出错

        7
  •  0
  •   Paul Ruane    15 年前

    相当于:

    Enumerator<Base> enumerator = new List<Base>().GetEnumerator();
    while (enumerator.MoveNext())
    {
        Derived obj = (Derived) enumerator.Current;
        // ...
    }
    

    因为您的列表是空的,所以内部块不会执行。

    如果它包含元素,那么就存在 InvalidCastException 如果任何元素不是类型 Derived .