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

本文关键字:循环 类型 之间 什么 区别 变量 元素 foreach | 更新日期: 2023-09-27 17:58:05

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;

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

为了帮助回答这个问题,让我们获取一些上下文,规范的前几行说:


如果成功,上述步骤将明确地生成集合类型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"answers"T"是什么。

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

所以这个:

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

那。。。首先,它获取enumerations当前值并将其强制转换为类型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是双的,V是整数。

我推荐阅读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通常包含开发人员已知的统一类型的元素。在一个没有泛型的世界里,除了类型系统告诉你之外,你通常必须提前知道这一点。因此,正如从对象到字符串的转换向编译器提示该值实际上是一个字符串一样,也是

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

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

元素类型为short,因为表达式的类型是short的数组。然而,尽管数组确实实现了IEnumerable<T>,但用于foreach的集合类型是纯旧的IEnumerable,其GetEnumerator()方法返回纯旧IEnumerator,并且其Current属性的类型为object。直接转换为int将生成InvalidCastException

对于short的数组,可以指定集合类型为IEnumerable<short>,但对于多维数组,这将中断,因为这些数组只实现非泛型IEnumerable