c# 迭代器實現

開發中若是想要本身實現一個集合數據接口而且能夠用foreach來遍歷該集合,通常須要實現2個類spa

IEnumerablecode

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}
複製代碼

IEnumeratorcdn

public interface IEnumerator
{
    bool MoveNext();
    void Reset();
    object Current { get; }
}
複製代碼

GetEnumerator()方法做用是須要獲得一個IEnumerator接口的實例對象

MoveNext() 方法做用是判斷此次是否能夠遍歷blog

Reset() 重置該迭代器的索引索引

Current 取出當前遍歷的數據接口

接下來編寫2個簡單的類來實現foreach的遍歷內存

public class MyList<TEntity> : IEnumerable<TEntity>
{
    private readonly int _initLength;
    private TEntity[] _list;
    
    public MyList()
    {
        _list = new TEntity[10];
        _initLength = 10;
    }
    
    public IEnumerator<TEntity> GetEnumerator()
    {
        return new MyEnumeratir(_list);
    }
    
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
    
    public void Add(TEntity entity)
    {
        if(entity==null)
            throw new NullReferenceException("添加的entity 對象爲Null");
        if (getNullLocation() == -1) ResetSize();
        int index = getNullLocation();
        _list[index] = entity;
    }
    
    private int getNullLocation()
    {
        for (int i = 0; i < _list.Length; i++)
        {
            if (_list[i] == null)
            {
                return i;
            }
        }
        return -1;
    }
    
    private void ResetSize()
    {
        TEntity[] newList = new TEntity[_list.Length+10];
        for (int i = 0; i < _list.Length; i++)
        {
             newList[i] = _list[i];
        }
        _list = newList;
    }
}

public class MyEnumerator<TEntity>:IEnumerator<TEntity>
{
    private TEntity[] _list;
    private int _index;
    private TEntity _current;
    
    public MyEnumerator(TEntity[] list)
    {
        _index = 1;
        _list = list;
    }
    
    public bool MoveNext()
    {
        _index++;
        if(_index==_list.Length) return false;
        TEntity obj = _list[_index];
        if(obj==null) return false;
        return true;
    }
    
    public void Reset()
    {
        _index= -1;
    }
    
    TEntity IEnumerator<TEntity>.Current
    {
        get
        {
            _current = _list[_index];
            return _current;
        }
    }
    
    public Object Current => _current;
}
複製代碼

上面的編寫的代碼簡單的實現了集合而且能夠隨意添加數據,接下來添加一下數據驗證這個集合類是否能夠正常遍歷開發

class Program
{
    public static void Main(string[] args) {
        MyList<Student> myList = new MyList<Student>();
    }
    myList.Add(new Student
    {
        Name = "周杰倫",
        Age = 18,
        Sex = "男"
    });
    myList.Add(new Student
    {
        Name = "張靚穎",
        Age = 20,
        Sex = "女"
    });
    myList.Add(new Student
    {
        Name = "雅典啊",
        Age = 1000,
        Sex = "女"
    });
    foreach (var student in myList)
    {
        Console.WriteLine($"姓名:{student.Name} ----- 年齡:{student.Age} ----- 性別:{student.Sex}");
    }
    Console.ReadLine();
}
public class Student
{
    public string Name { get; set; }
    public int Age { get; set; }
    public string Sex { get; set; }
}
複製代碼

控制檯正常輸出了添加的3條Student的實例對象get

上面的代碼中MyEnumerator 編寫了很長的代碼還須要判斷邊條條件,若是要投入到實際生產環境當中還須要編寫更多的邊界條件判斷非常麻煩。 不過C#開發團隊提供了一個更加簡單的語法糖來實現以上的操做那就是 關鍵字 yield

如今改寫一下MyList中的方法GetEnumerator()

public IEnumerator<TEntity> GetEnumerator() {
    //return new MyEnumeratir(_list);
    for(int i=0;i<=_list.Length-1;i++)
    {
        if(_list[i]!=null)
        {
            yield return _list[i];
        }
    }
}
複製代碼

這樣看起來代碼乾淨了很多哈 不過yield使用起來有幾條限制

1.不能將 yield return 語句置於 try-catch 塊中。 可將 yield return 語句置於 try-finally 語句的 try 塊中。
2.可將 yield break 語句置於 try 塊或 catch 塊中,但不能將其置於 finally 塊中。
3.若是 foreach 主體(在迭代器方法以外)引起異常,則將執行迭代器方法中的 finally 塊。

上面的實例是解釋了迭代器如何實現,實際開發中你這樣寫代碼得讓別人給罵死。
實際場景中yield也有很多應用的地方例如遍歷一個日期 讀取文本文件的字符,讀取一個內存流

相關文章
相關標籤/搜索