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

首頁 > 編程 > C# > 正文

C#特性-迭代器(上)及一些研究過程中的副產品

2020-01-24 02:13:20
字體:
來源:轉載
供稿:網友

提到迭代器我們不能不想到迭代器模式,那我就以迭代器模式作為開場白.

在我們的應用程序中常常有這樣一些數(shù)據(jù)結構:

它們是一個數(shù)據(jù)的集合,如果你知道它們內部的實現(xiàn)結構就可以去訪問它們,它們各自的內部存儲結構互不相同,各種集合有各自的應用場合.說到這里大家可能想出一大堆這樣的集合了:List,Hashtable,ArrayList等等。這些集合各自都有各自的個性,這就是它們存在的理由。但如果你想遍歷它你必須知道它內部的存儲細節(jié),作為一個集合元素,把內部細節(jié)暴露出來肯定就不好了,這樣客戶程序就不夠穩(wěn)定了,在你更換集合對象的時候,比如List不能滿足需求的時候,你換成Hashtable,因為以前的客戶程序過多的關注了List內部實現(xiàn)的細節(jié),所以不能很好的移植。而迭代器模式就是為解決這個問題而生的:

提供一種一致的方式訪問集合對象中的元素,而無需暴露集合對象的內部表示。
比如現(xiàn)在有這樣一個需求,遍歷集合內的元素,然后輸出,但是并不限定集合是什么類型的集合,也就是未來集合可能發(fā)生改變。

思考:

集合會發(fā)生改變,這是變化點,集合改變了,遍歷方法也改變,我們要保證遍歷的方法穩(wěn)定,那么就要屏蔽掉細節(jié)。找到了變化點那我們就將其隔離起來(一般使用interface作為隔離手段):假設所有的集合都繼承自ICollection接口,這個接口用來隔離具體集合的,將集合屏蔽在接口后面,作為遍歷我們肯定需要這樣一些方法:MoveNext,Current,既然ICollection負責數(shù)據(jù)存儲,職責又要單一,那么就新建立一個接口叫做Iterator吧,每種具體的集合都有自己相對應的Iterator實現(xiàn):

下面是一個簡易的實現(xiàn)代碼:

/// <summary>/// 集合的接口/// </summary>  public interface ICollection  {    int Count { get; }    /// <summary>    /// 獲取迭代器    /// </summary>    /// <returns>迭代器</returns>    Iterator GetIterator();  }  /// <summary>  /// 迭代器接口  /// </summary>  public interface Iterator  {    bool MoveNext();    object Current { get; }  }  public class List : ICollection  {    private const int MAX = 10;    private object[] items;    public List()    {       items = new object[MAX];    }    public object this[int i]    {      get { return items[i]; }      set { this.items[i] = value; }    }    #region ICollection Members    public int Count    {      get { return items.Length; }    }    public Iterator GetIterator()    {      return new ListIterator(this);    }    #endregion  }  public class ListIterator : Iterator  {    private int index = 0;    private ICollection list;    public ListIterator(ICollection list)    {      this.list = list;      index = 0;    }    #region Iterator Members    public bool MoveNext()    {      if (index + 1 > list.Count)        return false;      else      {         index++;        return true;      }    }    public object Current    {      get { return list[index]; }    }    #endregion  }  /// <summary>  /// 測試  /// </summary>  public class Program  {    static void Main()    {      ICollection list = new List();      Iterator iterator = list.GetIterator();      while (iterator.MoveNext())      {        object current = iterator.Current;      }    }}

看看最后的測試,是不是不管具體的集合如何改變,遍歷代碼都非常穩(wěn)定?而且擴展新的集合類也非常方便,只是添加代碼不會修改原來的代碼,符合開閉原則。當然,這么好的解決方案微軟當然不會放過,現(xiàn)在C# 2.0里已經內置了對迭代器的支持,看看System.Collections, System.Collections.Generic命名空間,所有的集合都實現(xiàn)了這個接口:IEnumerable,這個接口還有泛型的版本。注意到這個接口只有一個方法:IEnumerator GetEnumerator();,IEnumerator就是迭代器的接口,相當于我的實例里面的Iterator,它也有泛型的版本。
那么現(xiàn)在在.net里所有的集合類都可以這樣訪問了:

復制代碼 代碼如下:

IEnumerator ienumerator = list.GetEnumerator();
while(ienumerator.MoveNext())
{
    object current = ienumerator.Current;
}

但是這樣訪問也太麻煩了,所以C#里出現(xiàn)了foreach關鍵字,我們來看看foreach背后發(fā)生了什么?假如有如下的代碼:

public static void Main(){            ArrayList list = new ArrayList();      list.Add(1);      list.Add(2);      list.Add(3);      foreach (object item in list)      {        Console.WriteLine(item.ToString());      }}

下面是它對應的IL代碼:

.method private hidebysig static void Main() cil managed{  .entrypoint  .maxstack 2  .locals init (    [0] class [mscorlib]System.Collections.ArrayList list,    [1] object item,    [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000,    [3] class [mscorlib]System.IDisposable CS$0$0001)  L_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor()  L_0005: stloc.0   L_0006: ldloc.0   L_0007: ldc.i4.1   L_0008: box int32  L_000d: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)  L_0012: pop   L_0013: ldloc.0   L_0014: ldc.i4.2   L_0015: box int32  L_001a: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)  L_001f: pop   L_0020: ldloc.0   L_0021: ldc.i4.3   L_0022: box int32  L_0027: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object)  L_002c: pop   L_002d: ldloc.0   L_002e: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator()  L_0033: stloc.2   L_0034: br.s L_0048  L_0036: ldloc.2   L_0037: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()  L_003c: stloc.1   L_003d: ldloc.1   L_003e: callvirt instance string [mscorlib]System.Object::ToString()  L_0043: call void [mscorlib]System.Console::WriteLine(string)  L_0048: ldloc.2   L_0049: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()  L_004e: brtrue.s L_0036  L_0050: leave.s L_0063  L_0052: ldloc.2   L_0053: isinst [mscorlib]System.IDisposable  L_0058: stloc.3   L_0059: ldloc.3   L_005a: brfalse.s L_0062  L_005c: ldloc.3   L_005d: callvirt instance void [mscorlib]System.IDisposable::Dispose()  L_0062: endfinally   L_0063: call string [mscorlib]System.Console::ReadLine()  L_0068: pop   L_0069: ret   .try L_0034 to L_0052 finally handler L_0052 to L_0063}

從.locals init 那里可以看出編譯器為我們添加了兩個局部變量,一個就是迭代器。

復制代碼 代碼如下:

L_002d: ldloc.0
L_002e: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator()
L_0033: stloc.2

這三行代碼告訴我們,調用list的GetEnumerator()方法,獲取迭代器實例將其賦值給編譯器為我們添加的那個迭代器局部變量,接著是L_0034: br.s L_0048,
br.s這個指令是強制跳轉,我們接著看

復制代碼 代碼如下:

L_0048: ldloc.2
L_0049: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()

調用迭代器的MoveNext()方法,L_004e: brtrue.s L_0036 如果是true的話跳轉,

復制代碼 代碼如下:

L_0036: ldloc.2
L_0037: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
L_003c: stloc.1
L_003d: ldloc.1
L_003e: callvirt instance string [mscorlib]System.Object::ToString()
L_0043: call void [mscorlib]System.Console::WriteLine(string)

獲取當前值,然后輸出
 看到沒有,實際foreach后面干的事就是獲取迭代器,然后一個while循環(huán),不過這樣一些確實簡潔多了。
說到這里是不是

復制代碼 代碼如下:

IEnumerator ienumerator = list.GetEnumerator();
            while (ienumerator.MoveNext())
            {
                object item = ienumerator.Current;
                Console.WriteLine(item.ToString());
            }

復制代碼 代碼如下:

 foreach (object item in list)
            {
                Console.WriteLine(item.ToString());
            }

這兩樣代碼是一樣的呢?如果不一樣那推薦使用哪一個呢?當然是使用第二種,簡潔嘛,除了簡潔之外就沒有其它的了?細心讀者會發(fā)現(xiàn)上面的IL代碼,
在結束循環(huán)后還有一大塊,可我們的C#代碼中并沒有啊,接著分析:
.try L_0034 to L_0052 finally handler L_0052 to L_0063
這里說明從L_0034到L_0052是被放在try里面的,恰好這段代碼是循環(huán)體里的東西,L_0052到L_0063里是放在finally里的,看來foreach還給我們加了一
個try{}finally{}結構啊。那看看L_0052到L_0063里是什么東西吧:

復制代碼 代碼如下:

L_0052: ldloc.2
    L_0053: isinst [mscorlib]System.IDisposable
    L_0058: stloc.3
    L_0059: ldloc.3
    L_005a: brfalse.s L_0062
    L_005c: ldloc.3
    L_005d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0062: endfinally
    L_0063: call string [mscorlib]System.Console::ReadLine()

判斷迭代器對象是否是一個IDisposable實例,如果是,那就要調用它的Dispose()方法了(為啥它要實現(xiàn)IDisposable接口?那肯定這個迭代器里使用了一些非托管資源)。
看到了吧,foreach也真夠智能的,看來使用foreach的方式是比自己用while方式安全穩(wěn)定多了。

(PS:好像是扯遠了點,不過大家一起了解一下,呵呵,其實我當初也沒想說這個,不過后來看IL代碼有點不對勁,就當作個副產品吧)

C# 2.0里還出現(xiàn)個關鍵字yield,我看了半天MSDN也沒明白它的意思:

在迭代器塊中用于向枚舉數(shù)對象提供值或發(fā)出迭代結束信號。到現(xiàn)在還是沒明白,不過yield這個東西后面卻包含了很多東西,有一些非常“奇怪”的特性,
我稱之為奇怪的意思是與我們以前的思維有的不符,Linq的一些特質也是建立在這個特性之上的。關于yield的更多討論我想放在另外一篇文章中,因為我覺得有必要。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 蒲城县| 开鲁县| 富裕县| 惠州市| 建平县| 延川县| 竹山县| 沙湾县| 交城县| 洞头县| 二连浩特市| 广德县| 旺苍县| 义马市| 苏尼特左旗| 镇巴县| 乐清市| 马鞍山市| 贡觉县| 安义县| 黑水县| 镇坪县| 石城县| 颍上县| 宁都县| 漳平市| 三河市| 新巴尔虎右旗| 堆龙德庆县| 木兰县| 福贡县| 德昌县| 墨脱县| 新密市| 康保县| 远安县| 方正县| 澳门| 沂水县| 获嘉县| 阆中市|