日期:2013-02-09  浏览次数:20441 次

C#语言从VB中吸取了一个非常实用的foreach语句。对所有支持IEnumerable接口的类的实例,foreach语句使用统一的接口遍历其子项,使得以前冗长的for循环中繁琐的薄记工作完全由编译器自动完成。支持IEnumerable接口的类通常用一个内嵌类实现IEnumerator接口,并通过IEnumerable.GetEnumerator函数,允许类的使用者如foreach语句完成遍历工作。
这一特性使用起来非常方便,但需要付出一定的代价。Juval Lowy发表在MSDN杂志2004年第5期上的Create Elegant Code with Anonymous Methods, Iterators, and Partial Classes一文中,较为详细地介绍了C# 2.0中迭代支持和其他新特性。

首先,因为IEnumerator.Current属性是一个object类型的值,所以值类型(value type)集合在被foreach语句遍历时,每个值都必须经历一次无用的box和unbox操作;就算是引用类型(reference type)集合,在被foreach语句使用时,也需要有一个冗余的castclass指令,保障枚举出来的值进行类型转换的正确性。


以下为引用:

using System.Collections;

public class Tokens : IEnumerable
{
...
Tokens f = new Tokens(...);

foreach (string item in f)
{
Console.WriteLine(item);
}
...
}




上面的简单代码被自动转换为


以下为引用:

Tokens f = new Tokens(...);

IEnumerator enum = f.GetEnumerator();
try
{
do {
string item = (string)enum.get_Current(); // 冗余转换

Console.WriteLine(item);
} while(enum.MoveNext());
}
finally
{
if(enum is IDisposable) // 需要验证实现IEnumerator接口的类是否支持IDisposable接口
{
((IDisposable)enum).Dispose();
}
}




好在C# 2.0中支持了泛型(generic)的概念,提供了强类型的泛型版本IEnumerable定义,伪代码如下:


以下为引用:

namespace System.Collections.Generic
{
public interface IEnumerable<ItemType>
{
IEnumerator<ItemType> GetEnumerator();
}
public interface IEnumerator<ItemType> : IDisposable
{
ItemType Current{get;}
bool MoveNext();
}
}




这样一来即保障了遍历集合时的类型安全,又能够对集合的实际类型直接进行操作,避免冗余转换,提高了效率。


以下为引用:

using System.Collections.Generic;

public class Tokens : IEnumerable<string>
{
... // 实现 IEnumerable<string> 接口

Tokens f = new Tokens(...);

foreach (string item in f)
{
Console.WriteLine(item);
}
}




上面的代码被自动转换为


以下为引用:

Tokens f = new Tokens(...);

IEnumerator<string> enum = f.GetEnumerator();
try
{
do {
string item = enum.get_Current(); // 无需转换

Console.WriteLine(item);
} while(enum.MoveNext());
}
finally
{
if(enum) // 无需验证实现IEnumerator接口的类是否支持IDisposable接口,
// 因为所有由编译器自动生成的IEnumerator接口实现类都支持
{
((IDisposable)enum).Dispose();
}
}





除了遍历时的冗余转换降低性能外,C#现有版本另一个不爽之处在于实现IEnumerator接口实在太麻烦了。通常都是由一个内嵌类实现IEnumerator接口,而此内嵌类除了get_Current()函数外,其他部分的功能基本上都是相同的,如


以下为引用:

public class Tokens : IEnumerable
{
public string[] elements;

Tokens(string source, char[] delimiters)
{
// Parse the string into tokens:
elements = source.Split(delimiters);
}

public IEnumerator GetEnumerator()
{
return new TokenEnumerator(this);
}

// Inner class implements IEnumerator interface:
private class TokenEnumerator : IEnumerator
{
private int position = -1;
private Tokens t;

public TokenEnumerator(Tokens t)
{
this.t = t;
}

// Declare the MoveNext method required by IEnumerator:
public bool MoveNext()
{
if (position < t.elements.Length - 1)
{
position++;
return true;
}
else
{
return false;
}
}

// Declare the Reset method required by IEnumerator:
public void Reset()
{
position = -1;
}

// Declare the Current property required by IEnumerator:
pu