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

首頁(yè) > 編程 > C# > 正文

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

2019-10-29 21:47:48
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友
這篇文章主要介紹了C#特性-迭代器(上)及一些研究過(guò)程中的副產(chǎn)品,需要的朋友可以參考下
 

提到迭代器我們不能不想到迭代器模式,那我就以迭代器模式作為開(kāi)場(chǎng)白.

在我們的應(yīng)用程序中常常有這樣一些數(shù)據(jù)結(jié)構(gòu):

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

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

思考:

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

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

下面是一個(gè)簡(jiǎn)易的實(shí)現(xiàn)代碼:
 

  1. /// <summary> 
  2. /// 集合的接口 
  3. /// </summary> 
  4.   public interface ICollection 
  5.   { 
  6.     int Count { get; } 
  7.     /// <summary> 
  8.     /// 獲取迭代器 
  9.     /// </summary> 
  10.     /// <returns>迭代器</returns> 
  11.     Iterator GetIterator(); 
  12.   } 
  13.   /// <summary> 
  14.   /// 迭代器接口 
  15.   /// </summary> 
  16.   public interface Iterator 
  17.   { 
  18.     bool MoveNext(); 
  19.  
  20.     object Current { get; } 
  21.   } 
  22.  
  23.   public class List : ICollection 
  24.   { 
  25.     private const int MAX = 10; 
  26.     private object[] items; 
  27.     public List() 
  28.     {  
  29.       items = new object[MAX]; 
  30.     } 
  31.     public object this[int i] 
  32.     { 
  33.       get { return items[i]; } 
  34.       set { this.items[i] = value; } 
  35.     } 
  36.     #region ICollection Members 
  37.  
  38.     public int Count 
  39.     { 
  40.       get { return items.Length; } 
  41.     } 
  42.  
  43.     public Iterator GetIterator() 
  44.     { 
  45.       return new ListIterator(this); 
  46.     } 
  47.  
  48.     #endregion 
  49.   } 
  50.   public class ListIterator : Iterator 
  51.   { 
  52.     private int index = 0; 
  53.     private ICollection list; 
  54.     public ListIterator(ICollection list) 
  55.     { 
  56.       this.list = list; 
  57.       index = 0; 
  58.     } 
  59.     #region Iterator Members 
  60.  
  61.     public bool MoveNext() 
  62.     { 
  63.       if (index + 1 > list.Count) 
  64.         return false
  65.       else 
  66.       {  
  67.         index++; 
  68.         return true
  69.       } 
  70.     } 
  71.  
  72.     public object Current 
  73.     { 
  74.       get { return list[index]; } 
  75.     } 
  76.  
  77.     #endregion 
  78.   } 
  79.   /// <summary> 
  80.   /// 測(cè)試 
  81.   /// </summary> 
  82.   public class Program 
  83.   { 
  84.     static void Main() 
  85.     { 
  86.       ICollection list = new List(); 
  87.       Iterator iterator = list.GetIterator(); 
  88.       while (iterator.MoveNext()) 
  89.       { 
  90.         object current = iterator.Current; 
  91.       } 
  92.     } 
?

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

 

復(fù)制代碼代碼如下:

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

 

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

  1. public static void Main() 
  2. {       
  3.       ArrayList list = new ArrayList(); 
  4.       list.Add(1); 
  5.       list.Add(2); 
  6.       list.Add(3); 
  7.       foreach (object item in list) 
  8.       { 
  9.         Console.WriteLine(item.ToString()); 
  10.       } 
 

下面是它對(duì)應(yīng)的IL代碼:

 

  1. .method private hidebysig static void Main() cil managed 
  2.   .entrypoint 
  3.   .maxstack 2 
  4.   .locals init ( 
  5.     [0] class [mscorlib]System.Collections.ArrayList list, 
  6.     [1] object item, 
  7.     [2] class [mscorlib]System.Collections.IEnumerator CS$5$0000, 
  8.     [3] class [mscorlib]System.IDisposable CS$0$0001) 
  9.   L_0000: newobj instance void [mscorlib]System.Collections.ArrayList::.ctor() 
  10.   L_0005: stloc.0  
  11.   L_0006: ldloc.0  
  12.   L_0007: ldc.i4.1  
  13.   L_0008: box int32 
  14.   L_000d: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object
  15.   L_0012: pop  
  16.   L_0013: ldloc.0  
  17.   L_0014: ldc.i4.2  
  18.   L_0015: box int32 
  19.   L_001a: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object
  20.   L_001f: pop  
  21.   L_0020: ldloc.0  
  22.   L_0021: ldc.i4.3  
  23.   L_0022: box int32 
  24.   L_0027: callvirt instance int32 [mscorlib]System.Collections.ArrayList::Add(object
  25.   L_002c: pop  
  26.   L_002d: ldloc.0  
  27.   L_002e: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Collections.ArrayList::GetEnumerator() 
  28.   L_0033: stloc.2  
  29.   L_0034: br.s L_0048 
  30.   L_0036: ldloc.2  
  31.   L_0037: callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current() 
  32.   L_003c: stloc.1  
  33.   L_003d: ldloc.1  
  34.   L_003e: callvirt instance string [mscorlib]System.Object::ToString() 
  35.   L_0043: call void [mscorlib]System.Console::WriteLine(string
  36.   L_0048: ldloc.2  
  37.   L_0049: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext() 
  38.   L_004e: brtrue.s L_0036 
  39.   L_0050: leave.s L_0063 
  40.   L_0052: ldloc.2  
  41.   L_0053: isinst [mscorlib]System.IDisposable 
  42.   L_0058: stloc.3  
  43.   L_0059: ldloc.3  
  44.   L_005a: brfalse.s L_0062 
  45.   L_005c: ldloc.3  
  46.   L_005d: callvirt instance void [mscorlib]System.IDisposable::Dispose() 
  47.   L_0062: endfinally  
  48.   L_0063: call string [mscorlib]System.Console::ReadLine() 
  49.   L_0068: pop  
  50.   L_0069: ret  
  51.   .try L_0034 to L_0052 finally handler L_0052 to L_0063 
?

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

 

復(fù)制代碼代碼如下:

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

 

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

 

復(fù)制代碼代碼如下:

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

 

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

 

復(fù)制代碼代碼如下:

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)

 

獲取當(dāng)前值,然后輸出
 看到?jīng)]有,實(shí)際foreach后面干的事就是獲取迭代器,然后一個(gè)while循環(huán),不過(guò)這樣一些確實(shí)簡(jiǎn)潔多了。
說(shuō)到這里是不是

 

復(fù)制代碼代碼如下:

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

 

 

復(fù)制代碼代碼如下:

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

 

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

 

復(fù)制代碼代碼如下:

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()

 

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

(PS:好像是扯遠(yuǎn)了點(diǎn),不過(guò)大家一起了解一下,呵呵,其實(shí)我當(dāng)初也沒(méi)想說(shuō)這個(gè),不過(guò)后來(lái)看IL代碼有點(diǎn)不對(duì)勁,就當(dāng)作個(gè)副產(chǎn)品吧)

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

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


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 宁城县| 瑞金市| 梁山县| 贵德县| 铜鼓县| 卓尼县| 亳州市| 洞口县| 南昌县| 新沂市| 班玛县| 舒城县| 若尔盖县| 青川县| 栖霞市| 勐海县| 仙桃市| 伊通| 称多县| 衡水市| 新泰市| 藁城市| 崇文区| 于田县| 衡阳市| 华池县| 禄劝| 西昌市| 合江县| 天台县| 高清| 郁南县| 雅安市| 神农架林区| 民县| 尼木县| 同心县| 江城| 东城区| 芒康县| 曲阜市|