• 迭代器模式随想


    一、什么是迭代器模式?

    定义:提供一种顺序访问集合的方法,而不暴露集合内部的表示

    顺序访问,one by one(挨个访问),不暴露集合内部表示,反映了面向对象程序中的封装性。可以这么理解,一组模特从后台出场,一个接着一个,但是先出场的模特,未必是站在最前面的模特。换句话说,对于观众,你不知道后台模特的特定位置。为什么是顺序访问呢?因为迭代器模式采用的输出机制是内部决定好的,你无法决定。不像字典类型,我传不同的key,可以访问不同的value。我们访问列表,可以直接访问第i个元素,但是迭代器,你想要访问下一个元素,必须把当前的元素访问过后,才能到下一个元素。

    二、c#中的迭代器接口

     迭代器接口,可以手动实现调用,如下:

     1     public class MyIEnumerator : IEnumerator<string>
     2     {
     3         string[] types = { "下等马", "上等马", "中等马" };
     4 
     5         int cur = -1;
     6         public string Current
     7         {
     8             get
     9             {
    10                 return types[cur];
    11             }
    12         }
    13         object IEnumerator.Current
    14         {
    15             get
    16             {
    17                 return this.Current;
    18             }
    19         }
    20 
    21         public void Dispose()
    22         {
    23         }
    24 
    25         public bool MoveNext()
    26         {
    27             if (cur < types.Length - 1)
    28             {
    29                 cur++;
    30                 return true;
    31             }
    32             return false;
    33         }
    34 
    35         public void Reset()
    36         {
    37             cur = -1;
    38         }
    39     }

    话说田忌赛马,按一定的出场顺序赢得了齐威王,此策略是由孙膑提出的,再看看调用:

    1      MyIEnumerator m = new MyIEnumerator();
    2      while (true)
    3       {
    4           if (m.MoveNext()) { Console.WriteLine(m.Current); }
    5           else break;
    6       }

    显然手动编写代码,比较麻烦,搞不好还弄个数组越界,我们看看c#中可枚举接口:

     此接口只有一个实现迭代器的方法,我们知道凡是实现了这个接口的,都可以用foreach循环,我们把调用迭代器的方法改成foreach自动调用

     1    public class MyEnumerable : IEnumerable<string>
     2     {
     3         public IEnumerator<string> GetEnumerator()
     4         {
     5             return new MyIEnumerator();
     6         }
     7 
     8         IEnumerator IEnumerable.GetEnumerator()
     9         {
    10             return GetEnumerator();
    11         }
    12     }
    1   MyEnumerable a = new MyEnumerable();
    2    foreach (var item in a)
    3    {
    4        Console.WriteLine(item);
    5    }

    可能有人问了,你改成foreach循环和手动循环调用有什么区别?可以这么说吧,foreach进一步简化了调用,你不用控制循环什么时候结束,你也不要操心怎么访问下一个元素。

    可见迭代器的调用已经很优雅了,如果迭代器的创建能够简化,那么就更好了,c#中提供了yield关键字。

    1         public IEnumerator<string> GetEnumerator()
    2         {
    3             yield return "下等马";
    4             yield return "上等马";
    5             yield return "中等马";
    6         }

    通过6行代码,编译器就为我们创建好了迭代器,如下所示:

     1 public class MyEnumerable : IEnumerable<string>, IEnumerable
     2 {
     3     // Methods
     4     [IteratorStateMachine(typeof(<GetEnumerator>d__0))]
     5     public IEnumerator<string> GetEnumerator()
     6     {
     7         yield return "下等马";
     8         yield return "上等马";
     9         yield return "中等马";
    10     }
    11 
    12     IEnumerator IEnumerable.GetEnumerator()
    13     {
    14         return this.GetEnumerator();
    15     }
    16 
    17     // Nested Types
    18     [CompilerGenerated]
    19     private sealed class <GetEnumerator>d__0 : IEnumerator<string>, IDisposable, IEnumerator
    20     {
    21         // Fields
    22         private int <>1__state;
    23         private string <>2__current;
    24         public MyEnumerable <>4__this;
    25 
    26         // Methods
    27         [DebuggerHidden]
    28         public <GetEnumerator>d__0(int <>1__state)
    29         {
    30             this.<>1__state = <>1__state;
    31         }
    32 
    33         private bool MoveNext()
    34         {
    35             switch (this.<>1__state)
    36             {
    37                 case 0:
    38                     this.<>1__state = -1;
    39                     this.<>2__current = "下等马";
    40                     this.<>1__state = 1;
    41                     return true;
    42 
    43                 case 1:
    44                     this.<>1__state = -1;
    45                     this.<>2__current = "上等马";
    46                     this.<>1__state = 2;
    47                     return true;
    48 
    49                 case 2:
    50                     this.<>1__state = -1;
    51                     this.<>2__current = "中等马";
    52                     this.<>1__state = 3;
    53                     return true;
    54 
    55                 case 3:
    56                     this.<>1__state = -1;
    57                     return false;
    58             }
    59             return false;
    60         }
    61 
    62         [DebuggerHidden]
    63         void IEnumerator.Reset()
    64         {
    65             throw new NotSupportedException();
    66         }
    67 
    68         [DebuggerHidden]
    69         void IDisposable.Dispose()
    70         {
    71         }
    72 
    73         // Properties
    74         string IEnumerator<string>.Current
    75         {
    76             [DebuggerHidden]
    77             get
    78             {
    79                 return this.<>2__current;
    80             }
    81         }
    82 
    83         object IEnumerator.Current
    84         {
    85             [DebuggerHidden]
    86             get
    87             {
    88                 return this.<>2__current;
    89             }
    90         }
    91     }
    92 }

    有趣的是编译器用switch case 实现MoveNext方法,yield用状态机实现迭代器。

    三、理解yield关键字

    yield关键字,后面紧跟return 表达式或者break。return 表达式,并没有结束迭代器,只是暂时离开迭代器,在特定情况下(调用MoveNext方法时),又会进入迭代器。可以理解迭代器处于暂停或者挂起状态。break直接结束迭代。

    四、迭代器的其它用途

    Linq表达式的延迟加载,如果我们有一个数组,想通过Linq过滤一下:

      List<int> ages =new List<int> { 5, 17, 30, 40 };
      var yong = ages.Where(g => g < 18);
    
      ages.Add(2);
    
       foreach (var item in yong)
       {
          Console.WriteLine(item);
       }

    运行结果:

    Where扩展方法只是生成了一个迭代器,告诉这个迭代器原始的集合,以及如何筛选的方法,到调用的时候,才真正的过滤数据,这就是Linq延迟加载的原理。

  • 相关阅读:
    android个版本对应的SDK level,最新包括android10.0
    SQL语句 存在就更新不存在就插入
    forward和sendredirect
    JavaBean
    Cookie单点登录跨域问题
    JSP
    JSP内置对象
    Spring学习笔记
    事务
    AOP实现方式
  • 原文地址:https://www.cnblogs.com/wangqiang3311/p/11460373.html
Copyright © 2020-2023  润新知