IEnumerable與IEnumerator

IEnumerable接口

IEnumerable接口:實現該接口的類,代表該類下有能夠枚舉的元素

public interface IEnumerable
{
      //返回一個實現了IEnumerator接口的對象
      //個人理解:實現該接口的類,用其下哪一個可枚舉的元素實現該方法那麼當該類被枚舉時,將獲得該類下的某個可枚舉元素的元素
      IEnumerator GetEnumerator();
}
public class TestEnumerable : IEnumerable
  {
    //可枚舉元素
    private Student[] _students;
   
    public TestEnumerable(Student[] list)
    {
        _students = list;
    }
   
    public IEnumerator GetEnumerator()
    {
        //當TestEnumerator被枚舉時,將獲得_students的元素
        return _students.GetEnumerator();
    }
}
   
public class Student
{
    public string ID { get; set; }
   
    public string Name { get; set; }
   
    public Student(string id, string name)
    {
        ID = id;
        Name = name;
    }
}

調用代碼:express

public static void Main()
{
    Student[] students = new Student[3]
    {
        new Student("ID1", "Name1"),
        new Student("ID2", "Name2"),
        new Student("ID3", "Name3"),
    };
  
    TestEnumerable testEnumerable = new TestEnumerable(students);
  
    foreach (Student s in testEnumerable)
    {
        Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
    }
}

如上文所說, TestEnumerable類用foreach枚舉,獲得的每個對象就是上文提到的「能夠枚舉的元素(Student[])的每個對象」 。ide

 IEnumerator接口:枚舉的真正實現

public interface IEnumerator
{
    //獲取集合中的當前元素
    object Current{get;set;}
  
    //枚舉數推動到集合的下一個元素,當存在下一個對象時返回true,不存在時返回false
    bool MoveNext();
  
    //枚舉數設置爲其初始位置,該位置位於集合中第一個元素以前
    void Reset();
}
public class TestEnumerator : IEnumerator
{
  //指針,指向枚舉元素 (爲何這裏是-1,由於在MoveNext()中,_position++)
  private int _position = -1;
  private Student[] _students;
  public TestEnumerator() { }
  public TestEnumerator(Student[] list)
  {
    _students = list;
  }

  /// <summary>
  /// 獲取當前元素
  /// </summary>
  public object Current
  {
    get
    {
      return _students[_position];
    }
  }

  /// <summary>
  /// 移動到下一個元素
  /// </summary>
  /// <returns>成功移動到下一個元素返回:true;失敗返回:false</returns>
  public bool MoveNext()
  {
    _position++;
    return _position < _students.Length;
  }

  /// <summary>
  /// 重置到第一個元素
  /// </summary>
  public void Reset()
  {
    _position = -1;
  }
}

public class TestEnumerable2 : IEnumerable
{
  private Student[] _students;
  public TestEnumerable2(Student[] list)
  {
    _students = list;
  }

  public IEnumerator GetEnumerator()
  {
    return new TestEnumerator(_students);
  }
}

調用代碼:函數

public static void Main()
{
    Student[] students = new Student[3]
    {
        new Student("ID1", "Name1"),
        new Student("ID2", "Name2"),
        new Student("ID3", "Name3"),
    };
  
    TestEnumerable2 testEnumerator = new TestEnumerable2(students);
  
    foreach (Student s in testEnumerator)
    {
        Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
    }
}

IEnumerable接口與IEnumerator接口算介紹完畢了。測試

你們看IEnumerator接口的實現,就是實現一個屬性,兩個調整枚舉點的方法。若是能有一種方式,能自動幫咱們完成IEnumerator接口的實現,那麼將方便不少。this

在.NET Framework 3.5中有了yield,這個關鍵字能夠幫咱們自動生成實現IEnumerator的類。spa

yield

yield關鍵字,能夠幫咱們自動生成一個實現了IEnumerator的類,來完成枚舉。

下面,咱們先來看一下MSDN上的介紹指針

1.foreach 循環的每次迭代都會調用迭代器方法
2.迭代器方法運行到 yield return 語句時,會返回一個expression表達式並保留當前在代碼中的位置
3.當下次調用迭代器函數時執行從該位置從新啓動code

其實msdn這裏解釋的有點囉嗦了。簡單點說,就是迭代器運行到yield return語句時,將自動作兩件事。對象

1.記錄當前訪問的位置
2.當下次調用迭代器函數時執行從該位置從新啓動:MoveNext()blog

 下面咱們來看一段實例代碼:

public class TestEnumerable3 : IEnumerable
{
    private Student[] _students;
  
    public TestEnumerable3(Student[] list)
    {
        _students = list;
    }
      
    //實現與以前有不一樣
    public IEnumerator GetEnumerator()
    {
        //迭代集合
        foreach (Student stu in _students)
        {
            //返回當前元素,剩下的就交給yield了,它會幫咱們生成一個實現了接口IEnumerator的類
            //來幫咱們記住當前訪問到哪一個元素。
            yield return stu;
        }
    }
}

下面我貼出一段GetEnumerator()方法的IL代碼,來證實yield return 幫咱們自動生成了一個實現了IEnumerator接口的類

.method public final hidebysig newslot virtual
    instance class [mscorlib]System.Collections.IEnumerator GetEnumerator () cil managed
{
    // Method begins at RVA 0x228c
    // Code size 20 (0x14)
    .maxstack 2
    .locals init (
        [0] class TestBlog.TestEnumerable3/'<GetEnumerator>d__0',
                  
         //這句最重要,初始化了一個實現System.Collections.IEnumerator的class,存儲在索引1的位置
        [1] class [mscorlib]System.Collections.IEnumerator
    )
  
    IL_0000: ldc.i4.0
    IL_0001: newobj instance void TestBlog.TestEnumerable3/'<GetEnumerator>d__0'::.ctor(int32)
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: ldarg.0
    IL_0009: stfld class TestBlog.TestEnumerable3 TestBlog.TestEnumerable3/'<GetEnumerator>d__0'::'<>4__this'
    IL_000e: ldloc.0
    IL_000f: stloc.1
    IL_0010: br.s IL_0012
  
    IL_0012: ldloc.1
    IL_0013: ret
} // end of method TestEnumerable3::GetEnumerator

最後就是調用代碼:

static void Main(string[] args)
{
    Student[] students = new Student[3]
    {
        new Student("ID1", "Name1"),
        new Student("ID2", "Name2"),
        new Student("ID3", "Name3"),
    };
  
    TestEnumerable3 testEnumerable3 = new TestEnumerable3(students);
  
    foreach (Student s in testEnumerable3)
    {
        Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name);
    }
}

IEnumerable構建迭代方法

/// <summary>
/// IEnumerable構建迭代方法
/// <param name="_students">排序集合</param>
/// <param name="direction">排序順序(true:順序;false:倒序)</param>
public IEnumerable SortStudents(Student[] _students,bool direction)
{
    if (direction)
    {
        foreach (Student stu in _students)
        {
            yield return stu;
        }
    }
    else
    {
        for (int i = _students.Length; i != 0; i--)
        {
            yield return _students[i - 1];
        }
    }
}

細心的朋友必定發現了。這個方法的返回類型爲IEnumerable,而不像實現接口IEnumerable的GetEnumerator()方法返回類型是IEnumerator。這就是IEnumerable構建迭代方法須要注意的地方。

下面是測試代碼:

static void Main(string[] args)
{
    Student[] students = new Student[3]
    {
        new Student("ID1", "Name1"),
        new Student("ID2", "Name2"),
        new Student("ID3", "Name3"),
    };

   foreach (Student s in SortStudents(students,false)) { Console.WriteLine("ID:{0};Name:{1}", s.ID, s.Name); } }

感謝你們的耐心閱讀。

相關文章
相關標籤/搜索