|
來源:方小白
鏈接:http://www.cnblogs.com/fangyz/p/5597195.html
1.實(shí)例解析yiled的作用
最近參加java筆試題第一次見到y(tǒng)ield這個(gè)關(guān)鍵字,既然遇見了那肯定要掌握,下面是C#中關(guān)于yield關(guān)鍵字的總結(jié)。yield這個(gè)關(guān)鍵字作用于迭代器塊中,其最本質(zhì)的功能有2個(gè):一是“依次”向枚舉對(duì)象提供值,二是發(fā)出迭代結(jié)束信號(hào)。這兩個(gè)功能對(duì)應(yīng)的語句分別是yield return和yield break。
下面有2個(gè)小例子,分別沒有使用yield和有使用yield。先來看第一個(gè),當(dāng)我調(diào)試時(shí)顯然執(zhí)行到GetResult()方法時(shí)將會(huì)跳轉(zhuǎn)到方法內(nèi)部并且執(zhí)行完,接著再去執(zhí)行輸出當(dāng)前值語句。從結(jié)果可以看出第一個(gè)是0,說明返回的枚舉數(shù)所在的位置在集合中是0,接著才是我想要的遍歷數(shù)據(jù),也就是說只有調(diào)用MoveNext()后枚舉數(shù)才會(huì)繼續(xù)向前移動(dòng)得到下一個(gè)值,但是此時(shí)數(shù)據(jù)已全部加載到內(nèi)存。
再來看第二個(gè)例子,當(dāng)我調(diào)試到GetResultByYield()方法時(shí)我想進(jìn)入到這個(gè)方法內(nèi)部結(jié)果發(fā)現(xiàn)直接執(zhí)行下一句,根本就沒有進(jìn)入到GetResultByYield()方法內(nèi)部。此時(shí)發(fā)現(xiàn)result.Current是null,再加上前面根本都沒執(zhí)行方法內(nèi)部的代碼,因此我猜測(cè)此時(shí)集合都是空的。繼續(xù)調(diào)試,當(dāng)執(zhí)行MoveNext()方法時(shí)才去執(zhí)行GetResultByYield(),接著執(zhí)行到y(tǒng)ield return隨即返回main()方法輸出枚舉數(shù)所代表的集合中的值。
從上面可以看到只有調(diào)用MoveNext()需要用的時(shí)候才去執(zhí)行方法來獲得結(jié)果,不用的時(shí)候并不會(huì)有任何結(jié)果。這個(gè)地方編譯器會(huì)有一個(gè)狀態(tài)機(jī)用來保存迭代器的狀態(tài),以保證for循環(huán)時(shí)是從上一次yield return停止的狀態(tài)繼續(xù)執(zhí)行。這個(gè)過程就好比小方要喝一升的水,如果它用一個(gè)一升的杯子喝那么他要準(zhǔn)備一個(gè)一升的容器去飲水機(jī)裝滿一升的水。
如果小方喝到一半喝不完了,那接下來剩下的水則將被回收,這樣無論能不能喝完都必須準(zhǔn)備好一升的水,就像第一個(gè)例子?,F(xiàn)在讓杯子的容積縮小為0.2升,小方喝完一杯后再去飲水機(jī)去打水,每次只喝0.2升。這樣只有他要去喝的時(shí)候才去打水,如果他喝到一半不想喝了顯然浪費(fèi)的水比第一種方式多,這就像第二個(gè)例子。最后根據(jù)條件不再需要數(shù)據(jù)便可調(diào)用yield return來跳出while循環(huán),如果不寫yield break仍然可以正常結(jié)束迭代。
/// /// 不使用yield的時(shí)候 /// class Program { static void Main(string[] args) { //得到一個(gè)迭代結(jié)果 var result = GetResult(); //輸出當(dāng)前的值 Console.WriteLine(result.Current); Console.WriteLine('開始遍歷'); while (result.MoveNext()) { Console.WriteLine(result.Current); } Console.WriteLine('遍歷結(jié)束'); Console.ReadLine(); } //不使用yield來進(jìn)行迭代 static IEnumeratorint> GetResult() { var arr = new int[] { 1, 6, 8, 12,15}; Listint> list = new Listint>(); foreach (int item in arr) { if (item 12) list.Add(item); } return list.GetEnumerator(); } }
/// /// 使用yield關(guān)鍵字 /// class Program { static void Main(string[] args) { //得到一個(gè)迭代結(jié)果 var result = GetResultByYield(); //輸出當(dāng)前的值 Console.WriteLine(result.Current); Console.WriteLine('開始遍歷'); while (result.MoveNext()) { Console.WriteLine(result.Current); } Console.WriteLine('遍歷結(jié)束'); Console.ReadLine(); } //使用yield來進(jìn)行迭代 static IEnumerator GetResultByYield() { var arr = new int[] { 1,6,8,12,15}; foreach (var item in arr) { yield return item; if (item == 12) yield break; } } }
輸出結(jié)果如下:


2.深入yield
將上面第二個(gè)例子放入Reflector工具中,便得到了下面三段代碼。第一段是完整的Pragrom類的C#代碼,第二段是d__0密封類的C#展開代碼,第三段是GetResultByYield()方法的IL代碼。在第一段代碼中可以看到系統(tǒng)自動(dòng)生成了一個(gè)d__0密封類,它里面聲明了一些名字很奇怪的字段,不過我們可以很清楚的看到這個(gè)類里面有最重要的MoveNext()方法和Current屬性。
第二段代碼則是這個(gè)密封類的C#展開代碼,到這里不知道讀者有沒有和我當(dāng)初一樣的疑問:為什么要自動(dòng)生成一個(gè)密封類呢?答案就在第三段代碼中,可以看到在GetResultByYield()方法中并沒有遍歷數(shù)組,甚至都沒有看到創(chuàng)建數(shù)組的newarr指令,而是newobj創(chuàng)建了d__0密封類的實(shí)例對(duì)象。這也正是前面調(diào)試的時(shí)候?yàn)槭裁锤揪蜎]進(jìn)去GetResultByYield()方法的原因,因?yàn)檎嬲娴膶?shí)現(xiàn)代碼是在密封類里面的MoveNext()方法中。前面還提到y(tǒng)ield是按需所取,因此需要一個(gè)狀態(tài)機(jī)來記錄每次yield return的狀態(tài)。
在MoveNext()方法中由于密封類構(gòu)造函數(shù)傳進(jìn)去的是一個(gè)0(在第三段代碼中可以看到),因此第一次進(jìn)入到MoveNext方法時(shí)this.__state=0。此時(shí)current字段由于沒賦值因此就是null了。接著創(chuàng)建數(shù)組并開始一個(gè)while循環(huán)(原來foreach就是while循環(huán)),在循環(huán)中給current字段賦值并讓state字段值為2,最后返回true。拿Current屬性時(shí)就是拿while循環(huán)中給current賦的值,再次進(jìn)入這個(gè)方法內(nèi)此時(shí)state等于2于是跳轉(zhuǎn)到Label_0090,也就是進(jìn)入while語句塊中繼續(xù)循環(huán),這就是按需所取的原理。當(dāng)遇到y(tǒng)ield break后會(huì)先執(zhí)行Dispose釋放資源,再執(zhí)行break語句跳出循環(huán)??梢钥吹缴鲜鲞@個(gè)過程就是一個(gè)狀態(tài)機(jī),而這個(gè)密封類是為建立一個(gè)狀態(tài)機(jī)來生成的,現(xiàn)在我們自己也可以寫出一個(gè)狀態(tài)機(jī)了。
internal class Program { // Methods public Program(); private static IEnumerator GetResultByYield(); private static void Main(string[] args); // Nested Types [CompilerGenerated] private sealed class d__0 : IEnumeratorobject>, IEnumerator, IDisposable { // Fields private int 1__state; private object 2__current; public int[] 7__wrap4; public int 7__wrap5; public int[] 5__1; public int 5__2; // Methods [DebuggerHidden] public d__0(int 1__state); private void m__Finally3(); private bool MoveNext(); [DebuggerHidden] void IEnumerator.Reset(); void IDisposable.Dispose(); // Properties object IEnumeratorobject>.Current { [DebuggerHidden] get; } object IEnumerator.Current { [DebuggerHidden] get; } } }
private sealed class d__0 : IEnumeratorobject>, IEnumerator, IDisposable { // Fields private int 1__state; private object 2__current; public int[] 7__wrap4; public int 7__wrap5; public int[] 5__1; public int 5__2; // Methods [DebuggerHidden] public d__0(int 1__state) { this.1__state = 1__state; } private void m__Finally3() { this.1__state = -1; } private bool MoveNext() { try { switch (this.1__state) { case 0: this.1__state = -1; this.5__1 = new int[] { 1, 6, 8, 12, 15 }; this.1__state = 1; this.7__wrap4 = this.5__1; this.7__wrap5 = 0; while (this.7__wrap5 this.7__wrap4.Length) { this.5__2 = this.7__wrap4[this.7__wrap5]; this.2__current = this.5__2; this.1__state = 2; return true; Label_0090: this.1__state = 1; if (this.5__2 == 12) { this.System.IDisposable.Dispose(); break; } this.7__wrap5++; } this.m__Finally3(); break; case 2: goto Label_0090; } return false; } fault { this.System.IDisposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } void IDisposable.Dispose() { switch (this.1__state) { case 1: case 2: this.m__Finally3(); break; } }
// Properties object IEnumeratorobject>.Current { [DebuggerHidden] get { return this.2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return this.2__current; } } }
.method private hidebysig static class [mscorlib]System.Collections.IEnumerator GetResultByYield() cil managed { .maxstack 1 .locals init ( [0] class ConsoleApplication1.Program/d__0 d__, [1] class [mscorlib]System.Collections.IEnumerator enumerator) L_0000: ldc.i4.0 L_0001: newobj instance void ConsoleApplication1.Program/d__0::.ctor(int32) L_0006: stloc.0 L_0007: ldloc.0 L_0008: stloc.1 L_0009: br.s L_000b L_000b: ldloc.1 L_000c: ret }
3.單例模式
單例模式?jīng)]什么好說的,當(dāng)然如果深挖應(yīng)該也是大有學(xué)問,其中我覺得比較好的一種寫法如下。單例模式的代碼我看過多次不過卻沒怎么寫,結(jié)果真真寫的時(shí)候再加上時(shí)間又有點(diǎn)緊最后寫的一塌糊涂。以后寫代碼要興平氣和地去寫,急躁的狀態(tài)寫不出什么好代碼。當(dāng)然總會(huì)有煩躁的時(shí)候,所以只能多寫代碼來讓自己寫出高質(zhì)量的代碼成為一種習(xí)慣!
class A { private static A instance = new A(); public static A Instance { get { return A.instance; } } }
|