1. 程式人生 > >【5min+】你怎麼穿著品如的衣服?IEnumerable AND IEnumerator

【5min+】你怎麼穿著品如的衣服?IEnumerable AND IEnumerator

系列介紹

簡介

【五分鐘的dotnet】是一個利用您的碎片化時間來學習和豐富.net知識的博文系列。它所包含了.net體系中可能會涉及到的方方面面,比如C#的小細節,AspnetCore,微服務中的.net知識等等。

場景

您可以在下班坐地鐵的時候,拿出手機逛一逛部落格園,利用短短的五分鐘完成閱讀。

誕生緣由

  • 曾經學過的內容可能過不了多久就忘了,我們需要一些文章來幫我們查漏補缺。
  • 太長篇幅的文章看著滾動條就害怕了,我們可能更期望文字少的文章。
  • .net體系的內容太多了,平時也不知道該學哪些,我們可能需要一點點知識線索。

文章質量

當然,並不意味著它篇幅短就質量差。所謂麻雀雖小五臟俱全,我們會盡可能保證利用最少的文字去詳細的闡述內容。

正文

IEnumerable和IEnumerator,如果不仔細看,是不是都以為它們是同樣的一個單詞。特別是我們習慣了每天看大量的中文,這種只是很小區別的單詞更是容易犯錯。

在.NET的世界裡好像有這種類似單詞的情況還真的不少,比如Authentication和Authorization(認證和授權)。記得第一次見著它倆的時候,我說怎麼看了半天怎麼第一部分是它,第二個部分還是它?甚至我一度以為它們是同一個東西。(關於認證和授權將在後期為大家介紹。)

好的,回到今天的主題:IEnumerable和IEnumerator。目前我們知道它倆是不一樣的東西了,至少從單詞層面(ง •_•)ง。那麼在 DotNET 中,它們扮演著怎麼樣的角色呢?

先來看看它們的樣子:

IEnumerable說:我提供了公開列舉器,並且該列舉器支援在非泛型集合上進行簡單迭代的功能。

IEnumerator說:我提供了支援對非泛型集合進行簡單迭代的功能。

其實看介面的樣貌我們就大概能夠理解其中的奧祕了,IEnumerable 提供了可以迭代的能力,而這種能力是通過內部的可迭代物件來實現了,這個物件就是IEnumerator。

所以我們來想一下我們在.NET中經常用到的可迭代的物件有哪些呢? 是的,你可能第一個就會想到List。那我們就來檢視IList的介面繼承關係:

public interface IList : ICollection, IEnumerable

果不其然,它繼承了IEnumerable介面。那麼這種具有了可迭代能力的物件有什麼好處呢? foreach,沒錯,它可以享受foreach的語法糖啦。如果您瞭解過foreach的原理,您就知道,它其實是C#為我們對一下程式碼的包裝:

IEnumerator<string> enumeratorLst = IEnumerableClass.GetEnumerator();
while (enumeratorLst.MoveNext())
{
    Console.WriteLine(enumeratorLst.Current);
}

所以,一層一層的抽絲剝繭,原來脫掉了品如的衣服之後,內部居然是用了IEnumerator的各個屬性與方法之間的協作。如果您喜歡設計模式的話,您可能對這些方法再熟悉不過了,它是對迭代器模式的實現。

實際操作一波

雙色球搖獎大家都知道吧,就一個機器在那兒嘩嘩譁,然後不斷搖啊,迴圈啊,然後吐出球來。所以我們來建立這種的可迭代情況來試試吧:

當然哈,拿雙色球舉例只是為了好理解。賭博有風險,你懂的( ̄▽ ̄)"

先來把雙色球用我們C#的程式碼建立:

public class Ball
{
    //球號碼
    public int No { get; set; }
    //球顏色
    public string Color { get; set; }
}

然後按照上面說的,我們是不是要讓他擁有可迭代的能力,就是要讓球能夠擁有滾起來的能力,繼承IEnumerator來實現一個可迭代的雙色球迭代器物件:

public class BallEnum : IEnumerator
{
    public Ball[] _ball;
    int position = -1;

    public BallEnum(Ball[] ball)
    {
        _ball = ball;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _ball.Length);
    }

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

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Ball Current
    {
        get
        {
            try
            {
                return _ball[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

最後一步,給它穿上品如的衣服,將雙色球包裹,促使球去滾動,這個東西是什麼呢?好吧,它就是搖獎機,不用想它肯定是繼承了IEnumerable。

public class LotteryMachine : IEnumerable
{
    private Ball[] _balls;
    public LotteryMachine(Ball[] balls)
    {
        _balls = new Ball[balls.Length];

        for (int i = 0; i < balls.Length; i++)
        {
            _balls[i] = balls[i];
        }
    }

    // Implementation for the GetEnumerator method.
    IEnumerator IEnumerable.GetEnumerator()
    {
        return (IEnumerator)GetEnumerator();
    }

    public BallEnum GetEnumerator()
    {
        return new BallEnum(_balls);
    }
}

寫好了,來看看我們寫的這個程式碼怎麼呼叫:

//新增兩個雙色球
Ball[] balls = new Ball[2]
{
     new Ball() { No = 1, Color = "bule" },
     new Ball() { No = 2, Color = "red" }
};

//抬出我們的搖獎機,並把球放進去
LotteryMachine lotteryMachine = new LotteryMachine(balls);

//要動起來
foreach (var ball in lotteryMachine)
{
    Console.WriteLine($"{ball.Color} + {ball.No}");
}

用C#的foreach語法糖就可以迭代它啦,然後foreach in 出來的每一個物件的型別是什麼樣子的呢?(for迴圈中的ball)。

是的,它是Ball型別,那麼那個BallEnum型別呢? 它隱藏起來了,我們根本看不見啦。

總結

本來這次想給大家分享.net core中的ValueTask和Task的,但是由於時間有點匆忙,素材沒有收集完整,所以就只好等下次啦。

還是那句話,希望本篇文章沒有花費您太多的時間。(ง •_•)ง