C 基礎之yield與Singleton

2021-09-28 20:29:01 字數 3331 閱讀 2955

yield這個關鍵字作用於迭代器塊中,其最本質的功能有2個:一是「依次」向列舉物件提供值,二是發出迭代結束訊號。這兩個功能對應的語句分別是yield return和yield break。

下面有2個小例子,分別沒有使用yield和有使用yield。

先來看第乙個,當我除錯時顯然執行到getresult()方法時將會跳轉到方法內部並且執行完,接著再去執行輸出當前值語句。從結果可以看出第乙個是0,說明返回的列舉數所在的位置在集合中是0,接著才是我想要的遍歷資料,也就是說只有呼叫movenext()後列舉數才會繼續向前移動得到下乙個值,但是此時資料已全部載入到記憶體。再來看第二個例子,當我除錯到getresultbyyield()方法時我想進入到這個方法內部結果發現直接執行下一句,根本就沒有進入到getresultbyyield()方法內部。此時發現result.current是null,再加上前面根本都沒執行方法內部的**,因此我猜測此時集合都是空的。繼續除錯,當執行movenext()方法時才去執行getresultbyyield(),接著執行到yield return隨即返回main()方法輸出列舉數所代表的集合中的值。

從上面可以看到只有呼叫movenext()需要用的時候才去執行方法來獲得結果,不用的時候並不會有任何結果。這個地方編譯器會有乙個狀態機用來儲存迭代器的狀態,以保證for迴圈時是從上一次yield return停止的狀態繼續執行。這個過程就好比小方要喝一公升的水,如果它用乙個一公升的杯子喝那麼他要準備乙個一公升的容器去飲水機裝滿一公升的水。如果小方喝到一半喝不完了,那接下來剩下的水則將被**,這樣無論能不能喝完都必須準備好一公升的水,就像第乙個例子。現在讓杯子的容積縮小為0.2公升,小方喝完一杯後再去飲水機去打水,每次只喝0.2公升。這樣只有他要去喝的時候才去打水,如果他喝到一半不想喝了顯然浪費的水比第一種方式少,這就像第二個例子。最後根據條件不再需要資料便可呼叫yield return來跳出while迴圈,如果不寫yield break仍然可以正常結束迭代。

/// /// 不使用yield的時候

///

class program

console.writeline("遍歷結束");

console.readline();

}//不使用yield來進行迭代

static ienumeratorgetresult()

;listlist = new list();

foreach (int item in arr)

return list.getenumerator();}}

/// /// 使用yield關鍵字

///

class program

console.writeline("遍歷結束");

console.readline();

}//使用yield來進行迭代

static ienumerator getresultbyyield()

;foreach (var item in arr)}}

輸出結果如下:

將上面第二個例子放入reflector工具中,便得到了下面三段**。第一段是完整的pragrom類的c#**,第二段是d__0密封類的c#展開**,第三段是getresultbyyield()方法的il**。在第一段**中可以看到系統自動生成了乙個d__0密封類,它裡面宣告了一些名字很奇怪的字段,不過我們可以很清楚的看到這個類裡面有最重要的movenext()方法和current屬性。第二段**則是這個密封類的c#展開**,到這裡不知道讀者有沒有和我當初一樣的疑問:為什麼要自動生成乙個密封類呢?答案就在第三段**中,可以看到在getresultbyyield()方法中並沒有遍歷陣列,甚至都沒有看到建立陣列的newarr指令,而是newobj建立了d__0密封類的例項物件。這也正是前面除錯的時候為什麼根本就沒進去getresultbyyield()方法的原因,因為真真的實現**是在密封類裡面的movenext()方法中。前面還提到yield是按需所取,因此需要乙個狀態機來記錄每次yield return的狀態。

在movenext()方法中由於密封類建構函式傳進去的是乙個0(在第三段**中可以看到),因此第一次進入到movenext方法時this.<>__state=0。此時current欄位由於沒賦值因此就是null了。接著建立陣列並開始乙個while迴圈(原來foreach就是while迴圈),在迴圈中給current欄位賦值並讓state字段值為2,最後返回true。拿current屬性時就是拿while迴圈中給current賦的值,再次進入這個方法內此時state等於2於是跳轉到label_0090,也就是進入while語句塊中繼續迴圈,這就是按需所取的原理。當遇到yield break後會先執行dispose釋放資源,再執行break語句跳出迴圈。可以看到上述這個過程就是乙個狀態機,而這個密封類是為建立乙個狀態機來生成的,現在我們自己也可以寫出乙個狀態機了。

internal class program

object ienumerator.current }}

private sealed class d__0 : ienumerator, ienumerator, idisposable

private void <>m__finally3()

private bool movenext()

;this.<>1__state = 1;

this.<>7__wrap4 = this.5__1;

this.<>7__wrap5 = 0;

while (this.<>7__wrap5 < this.<>7__wrap4.length)

this.<>7__wrap5++;

}this.<>m__finally3();

break;

case 2:

goto label_0090;

}return false;

}fault

}[debuggerhidden]

void ienumerator.reset()

void idisposable.dispose()

}// properties

object ienumerator.current

}object ienumerator.current}}

.method private hidebysig static class [mscorlib]system.collections.ienumerator getresultbyyield() cil managed

Python基礎學習之六yield

協程的底層架構是在pep342 中定義,並在python2.5 實現的。python2.5 中,yield關鍵字可以在表示式中使用,而且生成器api中增加了 send value 方法。生成器可以使用.send 方法傳送資料,傳送的資料會成為生成器函式中yield表示式的值。協程是指乙個過程,這個過...

C 關鍵字之yield

說起yield,不得不先說說迭代器。迭代器是c 2.0中的新功能,有了它,我們就可以在自己的類或者結構中支援foreach迭代而不必實現整個ienumerable介面,我們只需要提供乙個迭代器,即可遍歷類中的資料結構。當編譯器檢測到迭代器時,它將自動生成ienumerable介面的current m...

C 關鍵字之yield 迭代器

今天講到迭代器,對迭代器中yield關鍵字做了研究。說起yield,不得不先說說迭代器。迭代器是c 2.0中的新功能,有了它,我們就可以在自己的類或者結構中支援foreach迭代而不必實現整個ienumerable介面,我們只需要提供乙個迭代器,即可遍歷類中的資料結構。當編譯器檢測到迭代器時,它將自...