您的位置:首页 > 编程语言 > C#

C#高级程序设计(七)——迭代

2012-08-27 15:09 183 查看
.NET使用IEnumerator接口和IEnumerable接口(以及它们的泛型版本)实现迭代器模式,迭代是LINQ的核心。

接口定义如下:

public interface IEnumerable
{
IEnumerator GetEnumerator();
}

public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}

public interface IEnumerable<out T> : IEnumerable
{
IEnumerator<T> GetEnumerator();
}

public interface IEnumerator<out T> : IDisposable, IEnumerator
{
T Current { get; }
}


一、C#1实现迭代方式

C#1通过实现接口实现迭代,下面是一个例子,可以指定起始的顺序来迭代集合,其中的一些注意点在注释中有所体现。

public class IterationSample : IEnumerable<string>
{
string[] values;
int startingPoint;

public IterationSample(string[] values, int startingPoint)
{
this.values = values;
this.startingPoint = startingPoint;
}
public IEnumerator<string> GetEnumerator()
{
// This is important to return a new instance of iterator
// so that each foreach loop will have its own iterator.
return new IterationSampleIterator(this);
}

// Class that implement IEnumerable<T> must also implement IEnumerable,
// here is a workaround to implement GetEnumerator method that defined
// in both IEnumerable<T> and IEnumerable.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

// Inner class can access to private members of its parent class.
class IterationSampleIterator : IEnumerator<string>
{
IterationSample parent;
int position;

internal IterationSampleIterator(IterationSample parent)
{
this.parent = parent;
position = -1;
}

public bool MoveNext()
{
if (position != parent.values.Length)
{
position++;
}

return position < parent.values.Length;
}

object IEnumerator.Current
{
get { return Current; }
}

public string Current
{
get
{
if (position == -1 ||
position == parent.values.Length)
{
throw new InvalidOperationException();
}
int index = position + parent.startingPoint;
index = index % parent.values.Length;
return parent.values[index];
}
}

public void Reset()
{
position = -1;
}

public void Dispose()
{
}
}
}

string[] values = { "a", "b", "c", "d", "e" };
IterationSample collection = new IterationSample(values, 3);
foreach (object x in collection)
{
Console.WriteLine(x);
}


二、C#2实现迭代方式

C#2提供了yield关键字,使得迭代实现代码大大简化,下面使用yield方式来改写上面的实现。

public class IterationSample : IEnumerable<string>
{
string[] values;
int startingPoint;

public IterationSample(string[] values, int startingPoint)
{
this.values = values;
this.startingPoint = startingPoint;
}
public IEnumerator<string> GetEnumerator()
{
for (int index = 0; index < values.Length; index++)
{
yield return values[(index + startingPoint) % values.Length];
}
}

// Class that implement IEnumerable<T> must also implement IEnumerable,
// here is a workaround to implement GetEnumerator method that defined
// in both IEnumerable<T> and IEnumerable.
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}

string[] values = { "a", "b", "c", "d", "e" };
IterationSample collection = new IterationSample(values, 3);
foreach (object x in collection)
{
Console.WriteLine(x);
}


yield 关键字向编译器指示它所在的方法是迭代器块。 编译器生成一个类来实现迭代器块中表示的行为。 在迭代器块中,yield 关键字与 return 关键字结合使用,向枚举器对象提供值。 这是一个返回值,例如,在 foreach 语句的每一次循环中返回的值。 yield 关键字也可与 break 结合使用,表示迭代结束。

yield return <expression>;
yield break;


三、迭代器

迭代器是 C# 2.0 中的新功能。迭代器是方法、get 访问器或运算符,它使您能够在结构中支持 foreach 迭代,而不必实现整个 IEnumerable 接口。您只需提供一个迭代器,即可遍历类中的数据结构。当编译器检测到迭代器时,它将自动生成 IEnumerable 或 IEnumerable<T>
接口的相关迭代方法,进一步简化了迭代的开发。

public class Program
{
//using System.Collections;
public static IEnumerable Power(int number, int exponent)
{
int counter = 0;
int result = 1;
while (counter++ < exponent)
{
result = result * number;
yield return result;
}
}

static void Main()
{
// Display powers of 2 up to the exponent 8:
foreach (int i in Power(2, 8))
{
Console.Write("{0} ", i);
}
}
}
/*
Output:
2 4 8 16 32 64 128 256
*/


四、迭代器实现LINQ查询

下面的例子使用迭代器实现了LINQ查询中Where方法。

public static IEnumerable<T> Where<T>(IEnumerable<T> source,
Predicate<T> predicate)
{
if (source == null || predicate == null)
{
throw new ArgumentNullException();
}
return WhereImpl(source, predicate);
}
private static IEnumerable<T> WhereImpl<T>(IEnumerable<T> source,
Predicate<T> predicate)
{
foreach (T item in source)
{
if (predicate(item))
{
yield return item;
}
}
}
...
IEnumerable<string> lines = LineReader.ReadLines("../../FakeLinq.cs");
Predicate<string> predicate = delegate(string line)
{ return line.StartsWith("using"); };
foreach (string line in Where(lines, predicate))
{
Console.WriteLine(line);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: