Home Csharp迭代器
Post
Cancel

Csharp迭代器

迭代器相关接口

image-20230205200228945

IEnumerator 枚举数

  1. IEnumerator 中包含了遍历集合必需的方法,允许使用迭代器模式而不是索引模式遍历元素集合。
  2. 枚举数相当于一个“游标”,记录了当前的遍历状态,每个游标独立于其它的游标。
  3. 通过枚举数确定第一个和下一个元素,就不需要事先知道元素总数,也不需要按照索引获取元素。
MoveNext
  1. 从集合的一个元素移动到下一个元素,同时返回是否已经遍历完集合中的每个元素。
Current
  1. 只读属性,返回当前的元素。

IEnumerable 可枚举

  1. 所有的集合至少要实现 IEnumerable<T>(可枚举的) 接口。
  2. IEnumerable 唯一的作用就是通过 GetEnumerator 返回一个 IEnumerator 枚举数实例。IEnumerator 通常由一个嵌套类实现,以便访问到集合的内部变量;并维护每个独立游标的状态,以便支持同时维护多个游标的情况。

foreach

  1. “运行时”并不认识 foreach,C#编译器会对代码进行必要的转换。
    • 对于数组:依靠 Length 属性和数组索引操作符 [],把foreach转化为等价的 for 循环。
    • 对于集合:通过迭代器遍历整个集合。(不需要长度已知,也不需要可以按索引检索)
  2. foreach 循环变量是只读变量,循环期间禁止修改。
  3. 如果 IEnumerator 实现了 IDisposable 接口,退出 foreach 循环后会调用 Dispose() 方法清理状态。

标准查询

[[LINQ]]

foreach 循环中不能修改集合
  1. foreach变量并不是原本的集合元素,对 foreach 变量赋值并不能改变集合元素本身,会引起混淆。
  2. foreach循环期间也不能改变集合中元素的数量。如果修改元素个数,iterator中应该忽略还是集成新的修改?
  3. 综上,C#编译器禁用了foreach中修改集合。

Duck Typing

  1. C#编译器不要求一定要实现 IEnumerable/IEnumerable<T> 才能对一个数据类型进行遍历。只需要能够查找到能返回“包含Current属性和MoveNext()方法的一个类型”的GetEnumerator()方法。Duck typing按名称查找方法,而不依赖接口或显式方法调用。当 [[DuckTyping]] 找不到可枚举模式的恰当实现时,编译器才会检查集合是否实现了接口。

枚举数模式

  1. 为自定义类型实现迭代的功能, 实现 IEnumerable 接口。
  2. 如果某个类型实现了IEnumerable接口,就意味着它可以被迭代访问。
  3. 把被迭代对象和迭代器分开,使得多个迭代器可以同时独立地操作同一个序列。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// IEnumerable相当于数据库中的表
public class CountingEnumerable : IEnumerable<int>		
{
    // 因为C#不能以返回类型区分两个重载方法
    // 隐式实现 IEnumerator  
    public IEnumerator<int> GetEnumerator()
    {
        return new CountingEnumerator();
    }

    // 使用显示接口实现 IEnumerable
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    
    // IEnumerator 嵌套类相当于数据库中的游标
    private class CountingEnumerator : IEnumerator<int>		
    {
        private int current = -1;
        
        public bool MoveNext()
        {
            current++;
            return current < 10;
        }

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

        public int Current => current;

        object IEnumerator.Current => Current;

        public void Dispose()
        {
        }
    }
}

迭代器块

  1. 迭代器模式简化了枚举数模式的语法,C#编辑器会自动把其扩展成枚举数模式的代码。
  2. 使用 yield return 语句实现迭代器块方法。
  3. 当编译器看到迭代器块时,会为类创建一个嵌套类型作为它的游标,来正确记录块中的位置和局部变量的值。
  4. 迭代器块实际上可以让你在这个代码块所涉及的特定位置“暂停”当前执行流,并随后携带着同样的状态返回同一位置。
1
2
3
4
5
6
public IEnumerator GetEnumerator()
{
    for (int i = 0; i < values.Length; ++i) {
        yield return value[i];
    }
}
This post is licensed under CC BY 4.0 by the author.
Contents