C#特征-迭代器(上)及一些研討進程中的副產物。本站提示廣大學習愛好者:(C#特征-迭代器(上)及一些研討進程中的副產物)文章只能為提供參考,不一定能成為您想要的結果。以下是C#特征-迭代器(上)及一些研討進程中的副產物正文
提到迭代器我們不克不及不想到迭代器形式,那我就以迭代器形式作為收場白.
在我們的運用法式中經常有如許一些數據構造:
它們是一個數據的聚集,假如你曉得它們外部的完成構造便可以去拜訪它們,它們各自的外部存儲構造互不雷同,各類聚集有各自的運用場所.說到這裡年夜家能夠想出一年夜堆如許的聚集了:List,Hashtable,ArrayList等等。這些聚集各自都有各自的特性,這就是它們存在的來由。但假如你想遍歷它你必需曉得它外部的存儲細節,作為一個聚集元素,把外部細節裸露出來確定就欠好了,如許客戶法式就不敷穩固了,在你改換聚集對象的時刻,好比List不克不及知足需求的時刻,你換成Hashtable,由於之前的客戶法式過量的存眷了List外部完成的細節,所以不克不及很好的移植。而迭代器形式就是為處理這個成績而生的:
供給一種分歧的方法拜訪聚集對象中的元素,而無需裸露聚集對象的外部表現。
好比如今有如許一個需求,遍歷聚集內的元素,然後輸入,然則其實不限制聚集是甚麼類型的聚集,也就是將來聚集能夠產生轉變。
思慮:
聚集會產生轉變,這是變更點,聚集轉變了,遍歷辦法也轉變,我們要包管遍歷的辦法穩固,那末就要屏障失落細節。找到了變更點那我們就將其隔離起來(普通應用interface作為隔離手腕):假定一切的聚集都繼續自ICollection接口,這個接口用來隔離詳細聚集的,將聚集屏障在接口前面,作為遍歷我們確定須要如許一些辦法:MoveNext,Current,既然ICollection擔任數據存儲,職責又要單一,那末就新樹立一個接口叫做Iterator吧,每種詳細的聚集都有本身絕對應的Iterator完成:
上面是一個簡略單純的完成代碼:
/// <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; } } }
看看最初的測試,是否是不論詳細的聚集若何轉變,遍歷代碼都異常穩固?並且擴大新的聚集類也異常便利,只是添加代碼不會修正本來的代碼,相符開閉准繩。固然,這麼好的處理計劃微軟固然不會放過,如今C# 2.0裡曾經內置了對迭代器的支撐,看看System.Collections, System.Collections.Generic定名空間,一切的聚集都完成了這個接口:IEnumerable,這個接口還有泛型的版本。留意到這個接口只要一個辦法:IEnumerator GetEnumerator();,IEnumerator就是迭代器的接口,相當於我的實例外面的Iterator,它也有泛型的版本。
那末如今在.net裡一切的聚集類都可以如許拜訪了:
IEnumerator ienumerator = list.GetEnumerator();
while(ienumerator.MoveNext())
{
object current = ienumerator.Current;
}
然則如許拜訪也太費事了,所以C#裡湧現了foreach症結字,我們來看看foreach面前產生了甚麼?假設有以下的代碼:
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輪回,不外如許一些確切簡練多了。
說到這裡是否是
IEnumerator ienumerator = list.GetEnumerator();
while (ienumerator.MoveNext())
{
object item = ienumerator.Current;
Console.WriteLine(item.ToString());
}
和
foreach (object item in list)
{
Console.WriteLine(item.ToString());
}
這兩樣代碼是一樣的呢?假如紛歧樣那推舉應用哪個呢?固然是應用第二種,簡練嘛,除簡練以外就沒有其它的了?仔細讀者會發明下面的IL代碼,
在停止輪回後還有一年夜塊,可我們的C#代碼中並沒有啊,接著剖析:
.try L_0034 to L_0052 finally handler L_0052 to L_0063
這裡解釋從L_0034到L_0052是被放在try外面的,正好這段代碼是輪回體裡的器械,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()辦法了(為啥它要完成IDisposable接口?那確定這個迭代器裡應用了一些非托管資本)。
看到了吧,foreach也真夠智能的,看來應用foreach的方法是比本身用while方法平安穩固多了。
(PS:似乎是扯遠了點,不外年夜家一路懂得一下,呵呵,其實我現在也沒想說這個,不外後來看IL代碼有點纰謬勁,就看成個副產物吧)
C# 2.0裡還湧現個症結字yield,我看了半天MSDN也沒明確它的意思:
在迭代器塊頂用於向列舉數對象供給值或收回迭代停止旌旗燈號。到如今照樣沒明確,不外yield這個器械前面卻包括了許多器械,有一些異常“奇異”的特征,
我稱之為奇異的意思是與我們之前的思想有的不符,Linq的一些特質也是樹立在這個特征之上的。關於yield的更多評論辯論我想放在別的一篇文章中,由於我認為有需要。