2017年10月2日 星期一

C# : 使用迭代器 Iterators 產生自己的集合 Collection 清單

在這個練習中,我們來開發出一個類別,可以用於 foreach 陳述式中,也就是迭代器的能力,根據微軟的定義:迭代器 可用來逐步執行集合,例如清單和陣列。迭代器方法或 get 存取子會對集合執行自訂反覆運算
要客製化類別,使其具有迭代器能力,在 C# 中,我們僅需要實作 IEnumerable<T> 這個介面,在您的類別中,實作出 GetEnumerator 這個方法即可。
這裡,我們模擬一個情境,您需要透過網路抓取四個網頁內容,不過,你無法控制哪個網頁會先讀取完畢,因此,您將需要設您的類別,只要網頁內容讀取完成之後,就會回傳到用戶端;而在用戶端中,可以使用 foreach 陳述式來逐一讀取這些網頁內容。為了達到這樣的需求,我們是用 C# 工作的靜態方法 Task.WhenAny 來協助我們處理這樣需求。
public class DownloadWeb : IEnumerable<DownloadWeb.GetWebResult>
{
    public class GetWebResult
    {
        public int Length { get; set; }
        public string URL { get; set; }
        public int ms { get; set; }
    }

    public string[] Urls = new string[]
    {
        "https://tw.yahoo.com/",
        "http://www.msn.com/zh-tw/",
        "https://world.taobao.com/",
        "https://www.microsoft.com",
    }; 

    public IEnumerator<GetWebResult> GetEnumerator()
    {
        List<Task<GetWebResult>> tasks = new List<Task<GetWebResult>>();
        tasks.Add(GetWebContent(Urls[0]));
        tasks.Add(GetWebContent(Urls[1]));
        tasks.Add(GetWebContent(Urls[2]));
        tasks.Add(GetWebContent(Urls[3]));

        while (tasks.Count > 0)
        {
            var aTask = Task.WhenAny(tasks);
            tasks.Remove(aTask.Result);
            var result = aTask.Result;
            yield return result.Result;
        }
    }

    public async Task<GetWebResult> GetWebContent(string url)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        using (var client = new HttpClient())
        {
            var result = await client.GetStringAsync(url);
            sw.Stop();
            GetWebResult getWebResult = new GetWebResult()
            {
                Length = result.Length,
                URL = url,
                ms = sw.Elapsed.Milliseconds
            };
            return getWebResult;
        }
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotImplementedException();
    }
}
在我們要進行測試的時候,首先,我們產生出一個 DownloadWeb 物件,使用 foreach 陳述式,逐一取得讀取到的網頁內容。你可以嘗試重複執行這個練習程式,將會發現到,每次進行 foreach 陳述式的時候,回傳結果順序都不進相同。
DownloadWeb downloadWeb = new DownloadWeb();

foreach (var item in downloadWeb)
{
    Console.WriteLine($"{item.URL} ({item.ms}) : {item.Length}");
}

Console.WriteLine($"Press any key to Exist...{Environment.NewLine}");
Console.ReadKey();
執行結果
http://www.msn.com/zh-tw/ (241) : 46965
https://tw.yahoo.com/ (327) : 201659
https://world.taobao.com/ (349) : 220947
https://www.microsoft.com (4) : 1020
Press any key to Exist...

沒有留言:

張貼留言