詳解C#迭代器

来源:https://www.cnblogs.com/minotauros/archive/2019/02/26/10439094.html
-Advertisement-
Play Games

一、迭代器(Iterator)通過持有迭代狀態可以獲取當前迭代元素並且識別下一個需要迭代的元素,從而可以遍歷集合中每一個元素而不用瞭解集合的具體實現方式; 實現迭代器功能的方法被稱為迭代器方法,迭代器方法的返回值類型可以是以下4種介面類型中任意一種:位於命名空間System.Collections中 ...


  一、迭代器(Iterator)通過持有迭代狀態可以獲取當前迭代元素並且識別下一個需要迭代的元素,從而可以遍歷集合中每一個元素而不用瞭解集合的具體實現方式;

  實現迭代器功能的方法被稱為迭代器方法,迭代器方法的返回值類型可以是以下4種介面類型中任意一種:位於命名空間System.Collections中的IEnumerable、IEnumerator和位於命名空間System.Collections.Generic中的IEnumerable<T>、IEnumerator<T>,其中介面IEnumerable和IEnumerator代碼:

public interface IEnumerable
{
    IEnumerator GetEnumerator();
}
public interface IEnumerator
{
    object Current { get; }

    bool MoveNext();
    void Reset();
}

  ※對應泛型介面IEnumerable<T>和IEnumerator<T>代碼:

public interface IEnumerable<out T> : IEnumerable
{
    IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable, IEnumerator
{
    T Current { get; }
}

  ※介面IEnumerable中聲明瞭獲取IEnumerator類型對象的方法GetEnumerator(),介面IEnumerator中通過屬性Current和方法MoveNext()實現迭代器功能;自定義類型可以通過繼承介面IEnumerable擁有迭代器功能,而介面IEnumerator為迭代器功能提供具體實現,設計二者符合單一職責原則,也因此可以對同一個對象同時進行多次迭代;

  ※get訪問器也可以聲明為迭代器方法,但事件、構造函數和析構函數不可以聲明為迭代器方法;迭代器方法不能包含引用參數;

  二、迭代器方法的內部使用狀態機實現,但可以使用yield關鍵字快速實現迭代器方法,使用yield return語句添加需要迭代的元素,在首次迭代時,會一直執行到第一個yield return語句並保存當前迭代狀態,在接下來的每次迭代過程中都會從暫停的位置繼續執行到下一個yield return語句並保存迭代狀態(MoveNext()方法返回true並將當前迭代的值賦值給Current),直到到達迭代器的結尾(MoveNext()方法返回false)完成本次迭代;也可以在迭代過程中使用yield break語句立刻結束本次迭代:

IEnumerable<int> MyIterator()
{
    yield return 10;
    yield return 20;
}

  ※其中yield return語句的返回值需要可以隱式轉換為迭代器方法返回值IEnumerable<T>中類型參數的類型;

  ※這是編譯器為我們準備的一種語法糖,編譯器會將其轉換為使用狀態機實現的實現了介面IEnumerable<T>的嵌套類,查看迭代器方法的IL代碼,可以看到編譯器生成的狀態機嵌套類:

  三、對於實現了介面IEnumerable或IEnumerable<T>的類型的對象,可以使用foreach對其進行遍歷:

foreach (int item in MyIterator())
{
    //do…其中item為int類型,值分別為10和20
}

  ※其中item實際上是類型中GetEnumerator()方法返回值類型中的Current屬性,因此其類型即該屬性的類型,通常可以使用var表示,由編譯器進行推斷,例如對哈希表進行遍歷時:

foreach (var item in myHashTable)
{
    //do…其中item為DictionaryEntry類型,item.Key and item.Value
}

  ※由於屬性Current只有get訪問器,因此不能在foreach迴圈中對item進行賦值(=運算符操作)操作,但可以對其對象中的成員進行修改和調用,詳見;

  ※在遍歷內置集合的對象時,如果集合長度發生改變,則極有可能紊亂迭代過程,因此在迭代這些集合對象時不能進行任何修改集合長度的操作,否則會拋出異常InvalidOperationException;

  四、除使用foreach外,還可以手動進行迭代,對於實現了泛型介面的類型的對象在手動迭代時還需要使用using語句以在其迭代完成後調用Dispose()方法:

using (IEnumerator<int> enumerator = MyIterator().GetEnumerator())

{
    //此時myEnmerator.Current為其類型的預設值
    while (enumerator.MoveNext())
    {
        //do…其中可以通過enumerator.Current訪問迭代器的值
    }
}    

  1.對於只實現了介面IEnumerator或IEnumerator<T>的類型的對象,只能手動進行迭代:

IEnumerator<int> MyIterator()
{
    yield return 1;
    yield return 2;
}
using (IEnumerator<int> enumerator = MyIterator())
{
    while (enumerator.MoveNext())
    {
        //do…
    }
}

  2.可以通過實現介面IEnumerable或IEnumerable<T>來自定義迭代器類型,此時不必手動實現IEnumerator或IEnumerator<T>介面,編譯器會自動實現該介面中的Current屬性、MoveNext()、Reset()和Dispose()方法:

public class MyIterator : IEnumerable<int>
{
    public IEnumerator<int> GetEnumerator()
    {
        yield return 10;
        yield return 20;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        Console.WriteLine("Non-Generic GetEnumerator");
        return GetEnumerator(); //註意這裡不是yield return
        //或者手動進行迭代:
        //using (IEnumerator<int> iterator = this.GetEnumerator())
        //{
        //while (iterator.MoveNext())
        //{
        //yield return iterator.Current;
        //}
        //}
    }
}    

  ※此時,在使用foreach遍歷時,如果使用var,會由編譯器自動推斷item為int類型,並調用泛型介面的實現,如果將item的類型指定為object類型,則依然會調用泛型介面的實現,但會產生裝箱操作:

foreach (var item in new MyIterator()) //或顯式指定為int:int item
{
    Console.WriteLine(item); //10 20
}

  ※此時,在使用foreach遍歷時,如果將其轉換為IEnumerable類型的對象,則會調用非泛型介面的實現,編譯器將自動推斷item為object類型,並產生裝箱操作:

foreach (var item in (IEnumerable)new MyIterator()) //或顯式指定為object:object item
{
    Console.WriteLine(item); //Non-Generic GetEnumerator 10 20
}

  3.直接調用迭代器方法或迭代器類型中自動生成的Reset()方法時會拋出異常NotSupportedException,若要從頭開始重新迭代,必須獲取新的迭代器,或在迭代器類型中手動實現Reset()方法;

 


如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的認可是我寫作的最大動力!

作者:Minotauros
出處:https://www.cnblogs.com/minotauros/

本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在日常的日誌解析當中,我們可能需要實現對日誌中的某一特殊段進行信息的過濾, 下麵介紹一下我的常用方法。 針對這個日誌輸出信息,其實我主要關註的是下麵這個日誌段內的信息 那麼其實我就是想獲取含有'loops-fast'這個字元串的數據,而過濾掉其他的干擾數據,我分為以下幾個步驟實現 首先要確定邊界值, ...
  • 基本代碼結構 url.py: views.py: 說明: has_permission方法的返回值是布爾類型,True表示許可權通過,False表示許可權拒絕 上面的基本結構是做局部的類的許可權判斷方式,全局許可權判斷後文介紹。 permission_classes屬性變數同樣也是一個列表,列表元素是許可權判 ...
  • 題意 "題目鏈接" Sol 這題想還是不難想的,就是寫起來很麻煩,然後去看了一下loj的最短代碼表示只能Orz 首先不難發現一條性質:能夠選擇的區間一定是不斷收縮的,而且新的可選區間一定是舊區間的某個位置劃分而來的。 比如$A_{i 1} = x$,此時小於$x$的最大數為$l_{i 1}$,大於$ ...
  • 本文從零開始介紹如何使用 Supervisor,一步一步的從安裝環境到編寫托管服務文件,設置執行文件、執行文件目錄、日誌輸出配置等細節,最終成功將 .NET Core 應用程式托管到了 Supervisor 中 ...
  • 前文說道了Action的激活,這裡有個關鍵的操作就是Action參數的映射與模型綁定,這裡即涉及到簡單的string、int等類型,也包含Json等複雜類型,本文詳細分享一下這一過程。(ASP.NET Core 系列目錄) 一、概述 當客戶端發出一個請求的時候,參數可能存在於URL中也可能是在請求的 ...
  • 使用過代碼生成器的開發人員應該知道,通過代碼生成器生成項目的代碼,可以大大的減少重覆編碼的時間,提供項目開發的效率,將自己從繁雜重覆的代碼中解放出來。現在網路上也有很多的開源的代碼生成器或者使用比較廣泛的代碼生成器,如動軟代碼生成器相信不少開發人員使用過。有時候線上的代碼生成器生成的代碼未必百分之百 ...
  • 首先 在 EntityFrameworkCore中安裝 Microsoft.Extensions.Logging.Console 按照官方文檔 使用UseLoggerFactory 方法 (地址:https://docs.microsoft.com/zh-cn/ef/core/miscellaneo ...
  • <img src="../../img/20190224185111.png" alt="" id="zhaopian"/> //定義變數 var img = ""; //圖片點擊 $("#zhaopian").click(function () { //賦值獲取圖片路徑 img = $(this) ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...