Heim > Artikel > Backend-Entwicklung > C#-Grundlagen Yield und Singleton
1. Beispielanalyse der Rolle von yield
Ich habe das Schlüsselwort yield zum ersten Mal in einem schriftlichen Java-Test gesehen. Das Folgende ist eine Zusammenfassung des Schlüsselworts yield in C#. Das Schlüsselwort yield fungiert im Iteratorblock und seine wichtigsten Funktionen sind zwei: Eine besteht darin, dem Aufzählungsobjekt „sequentiell“ Werte bereitzustellen, und die andere besteht darin, das Ende der Iteration zu signalisieren. Die diesen beiden Funktionen entsprechenden Anweisungen sind Yield Return bzw. Yield Break.
Im Folgenden finden Sie zwei kleine Beispiele, ohne Yield und mit Yield. Schauen wir uns zunächst die erste an. Beim Debuggen war es offensichtlich, dass die GetResult()-Methode bei der Ausführung in das Innere der Methode springt, die Ausführung abschließt und dann die Anweisung für den aktuellen Ausgabewert ausführt. Aus den Ergebnissen ist ersichtlich, dass die erste 0 ist, was bedeutet, dass die Position der zurückgegebenen Aufzählungsnummer in der Sammlung 0 ist und dass sich dann die gewünschten Durchlaufdaten, dh die Aufzählungsnummer, erst ändern MoveNext() wird aufgerufen. Gehen Sie weiter, um den nächsten Wert zu erhalten, aber jetzt wurden alle Daten in den Speicher geladen.
Schauen wir uns noch einmal das zweite Beispiel an. Als ich die GetResultByYield()-Methode debuggte, stellte ich fest, dass ich den nächsten Satz nicht eingegeben hatte ()-Methode überhaupt. Zu diesem Zeitpunkt wurde festgestellt, dass result.Current null war und der Code in der Methode überhaupt nicht ausgeführt wurde, sodass ich vermutete, dass die Sammlung zu diesem Zeitpunkt leer war. Setzen Sie das Debuggen fort, führen Sie GetResultByYield() aus, wenn die MoveNext()-Methode ausgeführt wird, und führen Sie dann die Yield-Rückgabe aus und kehren Sie dann zur main()-Methode zurück, um den Wert in der durch den Enumerator dargestellten Menge auszugeben.
Wie Sie oben sehen können, wird die Methode nur dann ausgeführt, wenn ein Aufruf von MoveNext() erforderlich ist, um das Ergebnis zu erhalten. Wenn sie nicht verwendet wird, wird kein Ergebnis angezeigt. Hier verfügt der Compiler über eine Zustandsmaschine zum Speichern des Zustands des Iterators, um sicherzustellen, dass die for-Schleife die Ausführung in dem Zustand fortsetzt, in dem die letzte Yield-Rückgabe gestoppt wurde. Dieser Vorgang ist so, als ob Xiao Fang einen Liter Wasser trinken möchte. Wenn er aus einem Ein-Liter-Becher trinkt, muss er einen Ein-Liter-Behälter vorbereiten und ihn am Wasserspender mit einem Liter Wasser füllen.
Wenn Xiao Fang die Hälfte des Getränks trinkt und es nicht austrinken kann, wird der Rest des Wassers recycelt, sodass ein Liter Wasser zubereitet werden muss, unabhängig davon, ob er es austrinken kann, genau wie im ersten Beispiel. Lassen Sie nun das Volumen der Tasse auf 0,2 Liter schrumpfen. Nachdem Xiaofang eine Tasse getrunken hat, geht er zum Wasserspender, um Wasser zu holen, und trinkt jedes Mal nur 0,2 Liter. Auf diese Weise holt er nur dann Wasser, wenn er trinken möchte. Wenn er während des Trinkens nicht trinken möchte, wird dadurch offensichtlich mehr Wasser verschwendet als bei der ersten Methode, die dem zweiten Beispiel ähnelt. Schließlich können Sie je nach den Bedingungen yield return aufrufen, um aus der while-Schleife zu springen, wenn die Daten nicht mehr benötigt werden. Wenn Sie yield break nicht schreiben, können Sie die Iteration trotzdem normal beenden.
/// /// 不使用yield的时候 /// class Program { static void Main(string[] args) { //得到一个迭代结果 var result = GetResult(); //输出当前的值 Console.WriteLine(result.Current); Console.WriteLine("开始遍历"); while (result.MoveNext()) { Console.WriteLine(result.Current); } Console.WriteLine("遍历结束"); Console.ReadLine(); } //不使用yield来进行迭代 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关键字 /// class Program { static void Main(string[] args) { //得到一个迭代结果 var result = GetResultByYield(); //输出当前的值 Console.WriteLine(result.Current); Console.WriteLine("开始遍历"); while (result.MoveNext()) { Console.WriteLine(result.Current); } Console.WriteLine("遍历结束"); Console.ReadLine(); } //使用yield来进行迭代 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; } } }
Die Ausgabeergebnisse lauten wie folgt:
2 . Detaillierte Ausbeute
Fügen Sie das zweite Beispiel oben in das Reflector-Tool ein und Sie erhalten die folgenden drei Codeteile. Der erste Absatz ist der C#-Code der vollständigen Pragrom-Klasse, der zweite Absatz ist der C#-Erweiterungscode der versiegelten Klasse d__0 und der dritte Absatz ist der IL-Code der GetResultByYield()-Methode. Im ersten Codeabschnitt können Sie sehen, dass das System automatisch eine versiegelte d__0-Klasse generiert, die einige Felder mit seltsamen Namen deklariert. Wir können jedoch deutlich erkennen, dass diese Klasse die wichtigste MoveNext()-Methode und die Current-Eigenschaft enthält.
Der zweite Teil des Codes ist der C#-Erweiterungscode dieser versiegelten Klasse. Ich frage mich, ob Leser an dieser Stelle die gleiche Frage haben wie ich: Warum sollte eine versiegelte Klasse automatisch generiert werden? Die Antwort liegt im dritten Teil des Codes. Sie können sehen, dass das Array nicht in der Methode GetResultByYield() durchlaufen wird, und Sie sehen nicht einmal die newarr-Anweisung zum Erstellen des Arrays. Stattdessen erstellt newobj ein Instanzobjekt von d__0 versiegelte Klasse. Aus diesem Grund wurde die GetResultByYield()-Methode beim Debuggen überhaupt nicht eingegeben, da der eigentliche Implementierungscode in der MoveNext()-Methode in der versiegelten Klasse liegt. Wie bereits erwähnt, erfolgt die Ertragsentnahme auf Abruf, sodass eine Zustandsmaschine erforderlich ist, um den Status jeder Ertragsrückgabe aufzuzeichnen.
在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 { // 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.单例模式
单例模式没什么好说的,当然如果深挖应该也是大有学问,其中我觉得比较好的一种写法如下。单例模式的代码我看过多次不过却没怎么写,结果真真写的时候再加上时间又有点紧最后写的一塌糊涂。以后写代码要兴平气和地去写,急躁的状态写不出什么好代码。当然总会有烦躁的时候,所以只能多写代码来让自己写出高质量的代码成为一种习惯!
class A { private static A instance = new A(); public static A Instance { get { return A.instance; } } }
以上就是C#基础之yield与Singleton的内容,更多相关内容请关注PHP中文网(www.php.cn)!