《c#高级编程》第2章C#2.0中的更改(三)——迭代器
一、概念
C#迭代器(Iterator)是一种特殊类型的方法,它使得在使用循环遍历数据集合时更加简单和有效。使用迭代器可以通过简单地定义迭代器方法来自动实现枚举器模式。
当您需要访问一个数据集合中的每个元素时,可以使用迭代器来遍历该集合。C#中的迭代器通过yield关键字实现。yield语句用于指示方法返回一个序列,并返回一系列值。这些值会被逐个返回到调用方,并且方法的当前状态会保留下来,以便下次调用时继续执行。
以下是一个使用迭代器的简单示例,其中展示了如何使用它来遍历整数数组:
```csharp
static IEnumerable<int> GetNumbers()
{
int[] numbers = { 1, 2, 3, 4, 5 };
foreach (int number in numbers)
{
yield return number;
}
}
```
在这个示例中,GetNumbers()方法返回一个IEnumerable<int>类型的迭代器。在foreach循环中,使用yield return语句依次返回数组中的每个数字。
使用迭代器有以下几个好处:
1. 简化代码结构:使用迭代器使得代码结构更加清晰、简洁。
2. 节省内存:由于迭代器只是逐个返回序列中的元素,所以可以大大减少内存的使用。
3. 延迟计算:当使用yield return语句时,元素是按需生成的,这意味着在需要时才会计算。这可以提高性能,并允许处理非常大的数据集合。
4. 支持Linq查询:由于迭代器实现了IEnumerable和IEnumerator接口,因此它们可以与Linq查询一起使用。
总之,C#迭代器是一种非常有用的工具,它使得遍历和操作数据集合变得更加简单、高效和灵活。通过使用yield关键字,可以轻松地定义自己的迭代器方法,并利用其强大的功能来提高代码质量和性能。
二、使用场景
以下是使用C#迭代器的经典场景和演示代码:
- 遍历集合数据
使用 C# 迭代器可以更方便地遍历一个集合中的所有元素,而不必事先知道集合的大小。
public class MyList<T> : IEnumerable<T> { private List<T> _list; public MyList() { _list = new List<T>(); } public void Add(T item) { _list.Add(item); } public IEnumerator<T> GetEnumerator() { for (int i = 0; i < _list.Count; i++) { yield return _list[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // 使用 MyList 遍历元素 var list = new MyList<int>(); list.Add(1); list.Add(2); list.Add(3); foreach (var item in list) { Console.WriteLine(item); } 输出结果: 1 2 3
- 实现延迟加载
使用 C# 迭代器可以实现延迟加载,也就是说只有在需要访问某个元素时才会去加载它,从而提高程序的性能和效率。
public class LazyDataLoader<T> : IEnumerable<T> { private Func<IEnumerable<T>> _dataLoader; public LazyDataLoader(Func<IEnumerable<T>> dataLoader) { _dataLoader = dataLoader; } public IEnumerator<T> GetEnumerator() { foreach (var item in _dataLoader()) { yield return item; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // 使用 LazyDataLoader 延迟加载数据 var dataLoader = new LazyDataLoader<int>(() => Enumerable.Range(1, 100000)); foreach (var item in dataLoader) { Console.WriteLine(item); } 输出结果: 1 2 ... 99999 100000
- 处理大型数据集
如果数据集非常大,无法一次性全部加载到内存中,使用 C# 迭代器可以分批次地读取数据,从而降低内存消耗。
public class BigDataLoader<T> : IEnumerable<T> { private Func<IEnumerable<T>> _dataLoader; private int _batchSize; public BigDataLoader(Func<IEnumerable<T>> dataLoader, int batchSize) { _dataLoader = dataLoader; _batchSize = batchSize; } public IEnumerator<T> GetEnumerator() { var buffer = new T[_batchSize]; var count = 0; foreach (var item in _dataLoader()) { buffer[count++] = item; if (count == _batchSize) { for (int i = 0; i < count; i++) { yield return buffer[i]; } count = 0; } } for (int i = 0; i < count; i++) { yield return buffer[i]; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // 使用 BigDataLoader 分批次读取数据 var bigDataLoader = new BigDataLoader<int>(() => Enumerable.Range(1, 100000), 1000); foreach (var item in bigDataLoader) { Console.WriteLine(item); } 输出结果: 1 2 ... 99999 100000
- 实现自定义的遍历逻辑
有时候我们需要按照特定的顺序或方式遍历集合数据,使用 C# 迭代器可以方便地实现自定义的遍历逻辑。
public class EvenNumberGenerator : IEnumerable<int> { private int _start; private int _end; public EvenNumberGenerator(int start, int end) { _start = start % 2 == 0 ? start : start + 1; _end = end % 2 == 0 ? end : end - 1; } public IEnumerator<int> GetEnumerator() { for (int i = _start; i <= _end; i += 2) { yield return i; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // 使用 EvenNumberGenerator 遍历偶数 var evenNumbers = new EvenNumberGenerator(10, 20); foreach (var item in evenNumbers) { Console.WriteLine(item); } 输出结果: 10 12 14 16 18 20
- 简化代码结构
使用 C# 迭代器可以简化代码结构,使得代码更加易读易懂,从而提高代码的可维护性和可读性。
public class FibonacciSequence : IEnumerable<int> { private int _count; public FibonacciSequence(int count) { _count = count; } public IEnumerator<int> GetEnumerator() { int a = 0; int b = 1; for (int i = 0; i < _count; i++) { yield return a; int temp = a; a = b; b = temp + b; } } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } // 使用 FibonacciSequence 生成斐波那契数列 var fibonacci = new FibonacciSequence(10); foreach (var item in fibonacci) { Console.WriteLine(item); } 输出结果: 0 1 1 2 3 5 8 13 21 34