国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

.NetC#5.0規范:迭代器

2019-11-14 16:11:50
字體:
來源:轉載
供稿:網友

本文內容

  • 枚舉器 enumerator 接口 - IEnumerator
  • 可枚舉 enumerable 接口 - IEnumerable
  • 產生類型 yield type
  • 枚舉器 enumerator 對象 
  • 可枚舉 enumerable 對象 
  • 示例
  • 參考資料

本文只是 C# 5.0 規范中的內容,稍作調整,主要是下載 Demo 看看,這玩意用法挺多。

使用迭代器塊實現的函數成員稱為迭代器(iterator)。(所謂函數成員,包括方法、屬性、事件、索引器、用戶定義運算符、實例構造函數、靜態構造函數和析構函數。)

只要相應函數成員的返回類型是枚舉器接口 enumerator 或可枚舉接口 enumerable 之一,迭代器塊就可用作該函數成員的函數體。如下“音樂標題”類所示,有三個函數,有返回 IEnumerator 的,還有返回 IEnumerable 的,其中,返回 Reverse 函數可以反序迭代一個集合,而 Subset 函數可以迭代一個集合的子集:

public class MusicTitles
{
    string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" };
 
    public IEnumerator GetEnumerator()
    {
        for (int i = 0; i < 4; i++)
        {
            yield return names[i];
        }
    }
 
    public IEnumerable Reverse()
    {
        for (int i = 3; i >= 0; i--)
        {
            yield return names[i];
        }
    }
 
    public IEnumerable Subset(int index, int length)
    {
        for (int i = index; i < index + length; i++)
        {
            yield return names[i];
        }
    }
}

可以用如下方式迭代:

foreach (string title in titles)
{
    Console.WriteLine(title);
}
 
foreach (string title in titles.Reverse())
{
    Console.WriteLine(title);
}
 
foreach (string title in titles.Subset(2, 2))
{
    Console.WriteLine(title);
}

迭代器塊可以是 method-body、Operator-body 或 accessor-body,而不能將事件、實例構造函數、靜態構造函數和析構函數作為迭代器來實現。

當使用迭代器塊實現函數成員時,為該函數成員的形參列表指定任何 ref 或 out 形參將產生編譯時錯誤。

下載 Demo

枚舉器 enumerator 接口 - IEnumerator


枚舉器接口 (enumerator interface) 為非泛型接口 System.Collections.IEnumerator 和泛型接口 System.Collections.Generic.IEnumerator<T> 的所有實例化。

簡潔起見,將這些接口分別表示為 IEnumeratorIEnumerator<T>

可枚舉 enumerable 接口 - IEnumerable


可枚舉接口 (enumerable interface) 為非泛型接口 System.Collections.IEnumerable 和泛型接口 System.Collections.Generic.IEnumerable<T> 的所有實例化。

簡潔起見,將這些接口分別表示為 IEnumerableIEnumerable<T>

產生類型 yield type


迭代器產生一系列值,所有值的類型均相同。此類型稱為迭代器的產生類型 (yield type)。

  • 返回 IEnumeratorIEnumerable 的迭代器的產生類型是 object
  • 返回 IEnumerator<T> IEnumerable<T> 的迭代器的產生類型是 T

C# 1.0 使用 foreach 語句可以迭代集合,但創建枚舉器需要大量代碼。C# 2.0 添加了 yield 語句,便于創建枚舉器。

枚舉器 enumerator 對象 


如果返回枚舉器接口類型的函數成員是使用迭代器塊實現的,調用該函數成員不會立即執行迭代器塊中的代碼。而是先創建并返回一個枚舉器對象 (enumerator object)。此對象封裝了在迭代器塊中指定的代碼,并且在調用該枚舉器對象的 MoveNext 方法時執行該迭代器塊中的代碼。枚舉器對象具有下列特點:

  • 它實現了 IEnumeratorIEnumerator<T>,其中 T 為迭代器的產生類型。
  • 它實現了 System.IDisposable
  • 它以傳遞給該函數成員的實參值(如果存在)和實例值的副本進行初始化。
  • 它有四種可能的狀態:運行前 (before)、運行中 (running)、掛起 (suspended) 和運行后 (after),并且初始狀態為運行前 (before) 狀態。

枚舉器對象通常是編譯器生成的枚舉器類的一個實例,它封裝了迭代器塊中的代碼,并實現了枚舉器接口,但也可能實現其他方法。如果枚舉器類由編譯器生成,則該類將直接或間接嵌套在包含該函數成員的類中,它將具有私有可訪問性,并且它將具有一個供編譯器使用的保留名稱。

枚舉器對象可實現除上面指定的那些接口以外的其他接口。

下面的各節將描述由枚舉器對象所提供的 IEnumerableIEnumerable<T> 接口實現的 MoveNextCurrentDispose 成員的確切行為。

請注意,枚舉器對象不支持 IEnumerator.Reset 方法。調用此方法將導致引發 System.NotSupportedException

MoveNext 方法

枚舉器對象的 MoveNext 方法封裝了迭代器塊的代碼。調用 MoveNext 方法將執行迭代器塊中的代碼,并相應設置枚舉器對象的 Current 屬性。MoveNext 執行的具體操作取決于調用 MoveNext 時的枚舉器對象的狀態:

  • 如果枚舉器對象的狀態為運行前 (before),則調用 MoveNext 會:
    • 將狀態更改為運行中 (running)。
    • 將迭代器塊的形參(包括 this)初始化為實參值以及初始化該枚舉器對象時所保存的實例值。
    • 從頭開始執行迭代器塊,直到執行被中斷(如后文所述)。
  • 如果枚舉器對象的狀態為運行中 (running),則調用 MoveNext 的結果不確定。
  • 如果枚舉器對象的狀態為掛起 (suspended),則調用 MoveNext 將:
    • 將狀態更改為運行中 (running)。
    • 將所有局部變量和形參(包括 this)的值恢復為迭代器塊的執行上次掛起時保存的值。注意,這些變量所引用對象的內容可能自上次調用 MoveNext 之后已經發生更改。
    • 恢復執行緊跟在引起執行掛起的 yield return 語句后面的迭代器塊,并一直繼續,直到執行中斷(如后文所述)。
  • 如果枚舉器對象的狀態為運行后 (after),則調用 MoveNext 將返回 false。

當 MoveNext 執行迭代器塊時,可以采用四種方式來中斷執行:通過 yield return 語句、通過 yield break 語句、到達迭代器塊的末尾以及引發異常并將異常傳播到迭代器塊之外。

  • 當遇到 yield return 語句時:
    • 計算該語句中給出的表達式,隱式轉換為產生類型,并賦給枚舉器對象的 Current 屬性。
    • 迭代器體的執行被掛起。所有局部變量和形參(包括 this)的值被保存,此 yield return 語句的位置也被保存。如果 yield return 語句在一個或多個 try 塊內,則此時與之關聯的 finally 塊將會執行。
    • 枚舉器對象的狀態更改為掛起 (suspended)。
    • MoveNext 方法向其調用方返回 true,指示迭代成功前進至下一個值。
  • 當遇到 yield break 語句時:
    • 如果 yield break 語句在一個或多個 try 塊內,則與之關聯的 finally 塊將執行。
    • 枚舉器對象的狀態更改為運行后 (after)。
    • MoveNext 方法向其調用方返回 false,指示迭代完成。
  • 當遇到迭代器體的結束處時:
    • 枚舉器對象的狀態更改為運行后 (after)。
    • MoveNext 方法向其調用方返回 false,指示迭代完成。
  • 當引發異常并傳播到迭代器塊之外時:
    • 通過異常傳播機制執行迭代器體內的相應 finally 塊。
    • 枚舉器對象的狀態更改為運行后 (after)。
    • 異常繼續傳播至 MoveNext 方法的調用方。

Current 屬性

枚舉器對象的 Current 屬性將受迭代器塊中的 yield return 語句影響。

當枚舉器對象處于掛起 (suspended) 狀態時,Current 的值為上一次調用 MoveNext 時設置的值。當枚舉器對象處于運行前 (before)、運行中 (running) 或運行后 (after) 狀態時,訪問 Current 的結果不確定。

對于產生類型不是 object 的迭代器,通過枚舉器對象的 IEnumerable 實現來訪問 Current 的結果對應于通過枚舉器對象的 IEnumerator<T> 實現來訪問 Current 并將該結果強制轉換為 object。

Dispose 方法

Dispose 方法用于通過使枚舉器對象變為運行后 (after) 狀態來清除迭代。

  • 如果枚舉器對象的狀態為運行前 (before),則調用 Dispose 將把狀態更改為運行后 (after)。
  • 如果枚舉器對象的狀態為運行中 (running),則調用 Dispose 的結果不確定。
  • 如果枚舉器對象的狀態為掛起 (suspended),則調用 Dispose 將:
    • 將狀態更改為運行中 (running)。
    • 執行所有 finally 塊,就像最后執行的 yield return 語句是 yield break 語句一樣。如果這導致引發異常,并且異常傳播到迭代器體之外,則枚舉器對象的狀態設置為運行后 (after),并且將異常傳播到 Dispose 方法的調用方。
    • 將狀態更改為運行后 (after)。
  • 如果枚舉器對象的狀態為運行后 (after),則調用 Dispose 沒有任何作用。

可枚舉對象 enumerable


如果返回可枚舉接口類型的函數成員是使用迭代器塊實現的,調用該函數成員不會立即執行迭代器塊中的代碼。而是先創建并返回一個可枚舉對象 (enumerable object)。可枚舉對象的 GetEnumerator 方法返回一個封裝有迭代器塊中指定的代碼的枚舉器對象,當調用該枚舉器對象的 MoveNext 方法時,將執行迭代器塊中的代碼。可枚舉對象具有下列特點:

  • 它實現了 IEnumerableIEnumerable<T>,其中 T 為迭代器的產生類型。
  • 它以傳遞給該函數成員的實參值(如果存在)和實例值的副本進行初始化。

可枚舉對象通常是編譯器生成的可枚舉類的實例,它封裝了迭代器塊中的代碼,并實現了可枚舉接口,但也可能實現其他方法。如果可枚舉類由編譯器生成,則該類將直接或間接嵌套在包含該函數成員的類中,它將具有私有可訪問性,并且它將具有供編譯器使用的保留名稱。

可枚舉對象可實現除上面指定的那些接口以外的其他接口。具體而言,可枚舉對象還可實現 IEnumerator 和 IEnumerator<T>,從而使其既可作為可枚舉對象,也可作為枚舉器對象。在該類型的實現中,首次調用可枚舉對象的 GetEnumerator 方法時,將返回可枚舉對象本身。對可枚舉對象的 GetEnumerator 的后續調用(如果存在),將返回可枚舉對象的副本。因此,每個返回的枚舉器都有自己的狀態,一個枚舉器中的更改不會影響其他枚舉器。

GetEnumerator 方法

可枚舉對象實現了 IEnumerable 和 IEnumerable<T> 接口的 GetEnumerator 方法。這兩種 GetEnumerator 方法的實現是相同的,都是獲取并返回一個可用的枚舉器對象。枚舉器對象是以初始化該可枚舉對象時保存的實例值和實參值進行初始化的,此外,枚舉器對象函數如前所述。

示例


下面的 Stack<T> 類使用一個迭代器實現其 GetEnumerator 方法。

規范中的該示例有編譯錯誤,所以采用 MSDN 中的實例:

public class Stack<T> : IEnumerable<T>
{
    PRivate T[] items;
    private int count;
 
    public void Push(T item)
    {
        if (items == null)
        {
            items = new T[4];
        }
        else if (items.Length == count)
        {
            T[] newItems = new T[count * 2];
            Array.Copy(items, 0, newItems, 0, count);
            items = newItems;
        }
        items[count++] = item;
    }
 
    public T Pop()
    {
        T result = items[--count];
        items[count] = default(T);
        return result;
    }
 
    public IEnumerator<T> GetEnumerator()
    {
        for (int i = count - 1; i >= 0; --i)
            yield return items[i];
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
 
    public IEnumerable<T> TopToBottom
    {
        get { return this; }
    }
 
    public IEnumerable<T> BottomToTop
    {
        get
        {
            for (int index = 0; index <= count - 1; index++)
            {
                yield return items[index];
            }
        }
    }
 
    public IEnumerable<T> TopN(int itemsFromTop)
    {
        // Return less than itemsFromTop if necessary. 
        int startIndex = itemsFromTop >= count ? 0 : count - itemsFromTop;
 
        for (int index = count - 1; index >= startIndex; index--)
        {
            yield return items[index];
        }
    }
}

若用如下代碼:

Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
stack.Push(3);
stack.Push(4);
stack.Push(5);
foreach (int s in stack)
{
    Console.Write(s + " ");
}
stack.Pop();
Console.Write("/n");
foreach (int s in stack)
{
    Console.Write(s + " ");
}
Console.Write("/n");
foreach (int s in stack.TopToBottom)
{
    Console.Write(s + " ");
}
Console.Write("/n");
foreach (int s in stack.BottomToTop)
{
    Console.Write(s + " ");
}
Console.Write("/n");
foreach (int s in stack.TopN(2))
{
    Console.Write(s + " ");
}
 
Console.ReadKey();

運行結果:

5 4 3 2 1
4 3 2 1
4 3 2 1
1 2 3 4
4 3

參考資料


  • MSDN Iterators (C# and Visual Basic)
  • yield (C# Reference)
  • foreach, in (C# Reference)
  • How to: Access a Collection Class with foreach (C# Programming Guide)
  • C#5.0 規范-中文
  • C#5.0 規范-英文

 

下載 Demo


上一篇:[C#]ASCIIHelper

下一篇:webconfig初認識

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 绵竹市| 舟山市| 通化市| 临高县| 安顺市| 贡山| 太康县| 达孜县| 枣强县| 绥化市| 阳谷县| 泸溪县| 云阳县| 法库县| 咸阳市| 泾源县| 于田县| 错那县| 嵊泗县| 台江县| 四平市| 定兴县| 沅陵县| 台江县| 郴州市| 怀柔区| 曲沃县| 峨边| 聂拉木县| 册亨县| 根河市| 杭锦后旗| 黄大仙区| 波密县| 克拉玛依市| 通海县| 太湖县| 赞皇县| 和顺县| 鹿泉市| 云浮市|