1. yield の役割の分析例
最近 Java の筆記試験に参加して、yield キーワードを初めて見たので、これをマスターする必要があります。 C#のキーワード。 yield キーワードはイテレータ ブロック内で機能し、その最も重要な機能は 2 つあります。1 つは列挙オブジェクトに値を「順次」提供すること、もう 1 つは反復の終了を通知することです。これら 2 つの関数に対応するステートメントは、それぞれ yield return と yield Break です。
以下に 2 つの小さな例があります。1 つは収量なし、もう 1 つは収量ありです。まず最初のメソッドを見てみましょう。デバッグをしていたとき、GetResult() メソッドが実行されると、メソッドの内部にジャンプして実行が完了し、出力現在値ステートメントが実行されることがわかりました。結果から、最初の値が 0 であることがわかります。これは、返された列挙番号の位置がコレクション内で 0 であり、次に必要な走査データ、つまり列挙番号が変更されるまで変更されないことを意味します。 MoveNext() が呼び出されます。次の値を取得するために先に進みますが、データはすべてメモリにロードされています。
もう一度 2 番目の例を見てみましょう。GetResultByYield() メソッドをデバッグしたときに、このメソッドの内部に入ろうとしましたが、次の文を直接実行しており、GetResultByYield() メソッドに入っていないことがわかりました。全て。この時、result.Currentがnullであることが分かり、メソッド内のコードは全く実行されていなかったので、この時点ではコレクションは空だったのではないかと推測しました。デバッグを続行し、MoveNext() メソッドの実行時に GetResultByYield() を実行し、次に yield return を実行してから main() メソッドに戻って、列挙子によって表されるセット内の値を出力します。
上記のことから、MoveNext() の呼び出しが必要な場合にのみメソッドが実行されて結果が取得され、それが使用されない場合は結果が得られないことがわかります。ここで、コンパイラにはイテレータの状態を保存するステート マシンがあり、最後の yield return が停止した状態から for ループが確実に実行を継続できるようにします。このプロセスは、Xiao Fang が 1 リットルのコップから水を飲みたい場合、1 リットルの容器を用意し、ウォーター サーバーで 1 リットルの水を注ぐ必要があるのと似ています。
シャオファンが飲み物の半分を飲んで飲み切れなかった場合、残りの水はリサイクルされるため、最初の例と同様に、飲み切れるかどうかに関係なく、1リットルの水を準備する必要があります。次に、コップの容量を 0.2 リットルに減らします。Xiaofang はコップを 1 杯飲んだ後、ウォーター サーバーに水を取りに行きます。毎回 0.2 リットルしか飲みません。このようにすると、飲みたくないときにだけ水を汲むことになります。これは、最初の方法よりも明らかに多くの水を無駄にします。最後に、条件に応じて、データが必要なくなったときに、yield return を呼び出して while ループから抜け出すことができます。yield Break を記述しなくても、反復を正常に終了できます。
/// /// 不使用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; } } }
出力結果は次のとおりです:
2. yield をさらに深く掘り下げます
上記の 2 番目の例を Reflector ツールに入力すると、次の 3 つのコードが得られます。最初の段落は完全な Pragrom クラスの C# コード、2 番目の段落は d__0 シールされたクラスの C# 拡張コード、3 番目の段落は GetResultByYield() メソッドの IL コードです。コードの最初の部分では、システムが d__0 シールされたクラスを自動的に生成し、奇妙な名前を持ついくつかのフィールドを宣言していることがわかりますが、このクラスには最も重要な MoveNext() メソッドと現在の属性が含まれていることがはっきりとわかります。
2 番目のコードは、このシールド クラスの C# 拡張コードです。この時点で、読者の皆さんも私と同じ疑問を抱いているのではないでしょうか。なぜシールド クラスが自動的に生成される必要があるのでしょうか。答えは、コードの 3 番目の部分にあります。GetResultByYield() メソッドでは配列が走査されず、配列を作成する newarr 命令も表示されず、代わりに newobj が d__0 のインスタンス オブジェクトを作成します。封印されたクラス。実際の実装コードはシールされたクラスの MoveNext() メソッド内にあるため、デバッグ中に GetResultByYield() メソッドがまったく入力されなかったのはこのためです。前述したように、利回りはオンデマンドで取得されるため、各利回りリターンのステータスを記録するにはステート マシンが必要です。
在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)!