来自IEnumerable的foreach中特定类型的对象

我正在使用只实现非通用IEnumerableICollection的旧集合对象。 当我尝试将此对象与foreach在foreach表达式的LHS上给出更具体的类型时,该对象究竟发生了什么?

 // LegacyFooCollection implements non-generic IEnumerable LegacyFooCollection collection = GetFooCollection(); foreach (Foo f in collection) { // etc. } 

我知道(因为我已经尝试过了)当collection所有东西都是Foo类型时,这是安全的,但如果失败会发生什么?

C#编译器为您隐式执行强制转换。 在铸造方面(但仅限于那些术语1 ),它相当于:

 foreach (object tmp in collection) { Foo f = (Foo) tmp; ... } 

请注意,通用集合也会发生这种情况:

 List list = new List { "hello", "there", 12345 }; // This will go bang on the last element foreach (string x in list) { } 

这一点在C#4规范的第8.8.4节中有详细说明。

如果您使用的是.NET 3.5或更高版本,并且只想选择相应类型的项目,则可以使用Enumerable.OfType

 LegacyFooCollection collection = GetFooCollection(); foreach (Foo f in collection.OfType()) { // etc. } 

对于LegacyFooCollection ,这可能不是必需的,但是当您尝试查找(比方说)表单中的所有TextBox控件时,它可能很有用。


1差异是:

  • 在原始代码中, f是只读的; 在“转换”中它是可写的
  • 在原始代码中,如果捕获f您将(当前)捕获所有迭代中的单个变量,而不是“转换”中每次迭代的单独变量

此代码将创建可枚举到Foo的每个元素的运行时类型转换(强制转换)。 所以,如果你做了foreach (string f in collection)你会在运行时得到一个ClassCastException ,它第一次尝试将Foo引用转换为String引用。

它每次都会投射。 foreach循环只是使用IEnumerator方法。

ForEeach中的简短演员:

 foreach (MenuItem menuItem in treeView.ContextMenu.Items.Cast().OfType() { // Do stuff }