近来列入java笔试题第一次见到yield这个关键字,既然遇见了那肯定要控制,下面是C#中关于yield关键字的总结。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 { 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
将上面第二个例子放入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 { // 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的内容,更多相关内容请关注ki4网(www.ki4.cn)!