foreach循环中元素类型和循环变量类型之间有什么区别?

时间:2014-04-12 14:18:47

标签: c# foreach

C#规范说foreach声明将扩展为:

{
    E e = ((C)(x)).GetEnumerator();
    try {
        while (e.MoveNext()) {
            V v = (V)(T)e.Current; // <-- double cast. For what?
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

为什么以下内容不正确?

V v = (V)e.Current;

3 个答案:

答案 0 :(得分:2)

为了帮助回答这个问题让我们得到一些内容,规范的前几行说:


以上步骤,如果成功,则明确地生成集合类型C,枚举器类型E和元素类型T.表单的foreach语句

foreach (V v in x) embedded-statement

然后扩展到

{
    E e = ((C)(x)).GetEnumerator();
    try {
        while (e.MoveNext()) {
            V v = (V)(T)e.Current; // <-- double cast. For what?
            embedded-statement
        }
    }
    finally {
        … // Dispose e
    }
}

在此之前是关于如何确定'C','E'和'T'的整个部分。

我建议有兴趣的人阅读它,但重要的是V的类型可能与x上的枚举返回的类型不同,后者必须能够转换为类型T(但仍然可以是不同的类型)和T必须能够转换为V。

所以这个:

 V v = (V)(T)e.Current;

是否......首先它将枚举当前值并将其强制转换为类型T,然后将其强制转换为类型V.

例如,请考虑以下代码

void Main()
{
  double [] values = { 1.1,2.2,3.3,4.4,5.5 };

  foreach(int number in values)
  {
    Console.WriteLine(number);
  }     

}

在这种情况下,T为double,V为int。

答案 1 :(得分:1)

我建议阅读eric lipperts优秀博客文章:

http://ericlippert.com/2013/07/22/why-does-a-foreach-loop-silently-insert-an-explicit-conversion/

我引用了他的话,所以点数也可以归他所有,因为这就是他所有人:

  

答案是:foreach循环语义是在将泛型添加到语言之前设计的;极有可能的情况是,枚举的集合是ArrayList或其他集合,其中元素类型对于编译器是未知的,但是开发人员已知。 ArrayList很少包含int和字符串以及Exceptions和Customers;通常,ArrayList包含开发人员已知的统一类型的元素。在没有泛型的世界中,除了类型系统告诉你之外,你通常必须通过某种方式提前知道。所以就像从对象到字符串的强制转换是对编译器的提示,该值实际上是一个字符串,所以也是

答案 2 :(得分:1)

需要双重强制转换的一个简单示例是数组:

short[] array = { 1, 2 };
foreach (int i in array) {
  // ...
}

元素类型为short,因为表达式的类型是short的数组。但是,尽管数组确实实现了IEnumerable<T>,但是foreach的集合类型是普通的IEnumerable,其GetEnumerator()方法返回普通的IEnumerator,并且其Current属性的类型为object。直接投射到int会产生InvalidCastException

对于short数组,可以指定集合类型为IEnumerable<short>,但对于多维数组而言,这会破坏,因为那些只实现非泛型{{1 }}