zoukankan      html  css  js  c++  java
  • .Net学习难点讨论系列11 foreach与迭代器,枚举数与可枚举值

    本文主要整理于ASP.NET2.0开发指南部分内容、C#图解教程

    要使一个自定义的集合类可以与foreach一起工作,我们需要实现IEnumerable接口(GetEnumerator方法)或实现IEnumerator<T>接口。

    在.NET Framework枚举迭代相关类设计中IEnumerator是基础,其包含三个成员其中一个Current属性,两个函数Movenext与Reset。这些是一个集合对象可枚举的基础。而IEnumerable接口只包含一个GetEnumerator方法,作用就是返回一个IEnumerator对象。

    .NET2.0新增的泛型接口IEnumerator<T>实现了IEnumerator与IDisposal接口,该接口自身包含一个Current属性,该属性返回一个T类型的对象。我们实现此接口时,需要实现其自身Current属性及父接口IEnumerator与IDisposal的方法。这其中特别需要注意的是,我们需要显式实现IEnumerator中的Current属性:

    1 object System.Collections.IEnumerator.Current
    2 {
    3     get { throw new NotImplementedException(); }
    4 }

    另一个泛型接口IEnumerable<T>继承自IEnumerable接口,其自身包含一个GetEnumerator方法返回泛型版本的IEnumerator<T>对象。类似于实现IEnumerator<T>,在实现IEnumerable<T>时,除了实现返回IEnumerator<T>的GetEnumerator方法,还要显示实现IEnumerable中的GetEnumerator方法:

    1 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    2 {
    3     throw new NotImplementedException();
    4 }

    了解了这些后就很容易解释迭代器(yield)了,迭代器提供给我们最简单的创建一个IEnumerator<T>对象或IEnumerable<T>的途径,见下面的代码段:

     1 //返回泛型枚举数
     2 public IEnumerator<string> SampleMethod1()
     3 {
     4     yield return "tree";
     5     yield return "flower";
     6 }
     7 
     8 //返回泛型可枚举对象
     9 public IEnumerable<string> SampleMethod2()
    10 {
    11     yield return "tree";
    12     yield return "flower";
    13 }

    综合以上,下面看一个完整的例子:

     1 public class Persons : IEnumerable
     2 {
     3     public string[] m_Names;
     4     public Persons(params string[] Names)
     5     {
     6         m_Names = new string[Names.Length];
     7         Names.CopyTo(m_Names, 0);
     8     }
     9 
    10     //实现索引器
    11     public string this[int index]
    12     {
    13         get
    14         {
    15             return m_Names[index];
    16         }
    17         set
    18         {
    19             m_Names[index] = value;
    20         }
    21     }
    22 
    23     //实现IEnumerable成员
    24     //需返回一个IEnumerator类型的对象
    25     #region IEnumerable 成员
    26 
    27     IEnumerator IEnumerable.GetEnumerator()
    28     {
    29         return new PersonsEnumerator(this);
    30     }
    31 
    32     #endregion
    33 }
    34 
    35 public class PersonsEnumerator : IEnumerator
    36 {
    37     int index = -1;
    38     Persons P;
    39     public PersonsEnumerator(Persons P)
    40     {
    41         this.P = P;
    42     }
    43     
    44     #region IEnumerator 成员
    45 
    46     object IEnumerator.Current
    47     {
    48         get
    49         {
    50             return P.m_Names[index];
    51         }
    52     }
    53 
    54     bool IEnumerator.MoveNext()
    55     {
    56         int tempIndex = ++index;
    57         if (tempIndex >= P.m_Names.Length)
    58         {
    59             return false;
    60         }
    61         else
    62         {
    63             return true;
    64         }
    65     }
    66 
    67     void IEnumerator.Reset()
    68     {
    69         index = -1;
    70     }
    71     #endregion
    72 }

    使用(非泛型)迭代器的方式:

    1 static void Main()
    2 {
    3     Persons arrPersons = new Persons("Michel", "Rebecca", "Polaris");
    4     foreach (string s in arrPersons)
    5     {
    6         Console.WriteLine(s);
    7     }
    8 }

    在C# 2.0中我们可以通过新增的关键字 – yeild,来方便的实现一个迭代器。而且.NET2.0中新增的泛型可以使迭代器强类型化。

    一步步进行:首先我们将上述程序改写为用yield关键字实现(仍然是非泛型)。

     1 public class Persons : IEnumerable
     2 {
     3     public string[] m_Names;
     4     public Persons(params string[] Names)
     5     {
     6         m_Names = new string[Names.Length];
     7         Names.CopyTo(m_Names, 0);
     8     }
     9 
    10     #region IEnumerable 成员
    11 
    12     IEnumerator IEnumerable.GetEnumerator()
    13     {
    14         for (int i = 0; i < m_Names.Length; i++)
    15         {
    16             yield return m_Names[i];
    17         }
    18     }
    19 
    20     #endregion
    21 }

    接下来,我们将此方法改写为泛型的实现:

     1 public class Persons<T> : IEnumerable<T>
     2 {
     3     public T[] m_Names;
     4     public Persons(params T[] Names)
     5     {
     6         m_Names = new T[Names.Length];
     7         Names.CopyTo(m_Names, 0);
     8     }
     9 
    10     #region IEnumerable<T> 成员
    11 
    12     IEnumerator<T> IEnumerable<T>.GetEnumerator()
    13     {
    14         for (int i = 0; i < m_Names.Length; i++)
    15         {
    16             yield return m_Names[i];
    17         }
    18     }
    19 
    20     #endregion
    21 }

    泛型版迭代器的使用:

    1 static void Main()
    2 {
    3     Persons<string> arrPersons = new Persons<string>("Michel", "Rebecca", "Polaris");
    4     foreach (string s in arrPersons)
    5     {
    6         Console.WriteLine(s);
    7     }
    8 }

    上文基本列出了怎样使用yield实现一个迭代器。

    下面来说一下yield的一些使用方式:

    1. 终止yield迭代的方式

     1 IEnumerator IEnumerable.GetEnumerator()
     2 {
     3     for (int i = 0; i < m_Names.Length; i++)
     4     {
     5         yield return m_Names[i];
     6         if (i >= 1)
     7         {
     8             yield break;
     9         }
    10     }
    11 }

    2. 逆序迭代内容

    1 IEnumerator IEnumerable.GetEnumerator()
    2 {
    3     for (int i = (m_Names.Length - 1); i >= 0; --i)
    4     {
    5         yield return m_Names[i];
    6     }
    7 }

    3. 迭代器的另一种实现,返回实现IEnumerable<T>接口的对象,不实现GetEnumerator()方法。

     1 public class Persons<T>
     2 {
     3     private T[] m_Names;
     4     public Persons(params T[] Names)
     5     {
     6         m_Names = new T[Names.Length];
     7         Names.CopyTo(m_Names, 0);
     8     }
     9 
    10     public IEnumerable<T> GetPersons()
    11     {
    12         for (int i = 0; i < m_Names.Length; i++)
    13         {
    14             yield return m_Names[i];
    15         }
    16     }
    17 }
    18 
    19 class Program
    20 {
    21     static void Main()
    22     {
    23         Persons<string> arrPersons = new Persons<string>("Michel", "Rebecca", "Polaris");
    24         foreach (string s in arrPersons.GetPersons())
    25         {
    26             Console.WriteLine(s);
    27         }
    28     }
    29 }
  • 相关阅读:
    值得推荐的C/C++框架和库
    P2P技术基础: 关于TCP打洞技术
    Java FileInputStream与FileReader的区别
    如何理解java采用Unicode编码
    细说:Unicode, UTF-8, UTF-16, UTF-32, UCS-2, UCS-4
    程序员趣味读物:谈谈Unicode编码
    字符编码笔记:ASCII,Unicode 和 UTF-8
    Java并发编程:volatile关键字解析(转)
    java线程安全问题之静态变量、实例变量、局部变量
    Hystrix使用详解
  • 原文地址:https://www.cnblogs.com/lsxqw2004/p/1583829.html
Copyright © 2011-2022 走看看