Maison > Article > développement back-end > Rendement des bases de C# et Singleton
1. Exemple d'analyse du rôle du rendement
J'ai récemment vu le mot-clé rendement pour la première fois dans un test écrit Java. Depuis que je l'ai rencontré, je dois le maîtriser. du mot clé rendement en C#. Le mot-clé rendement agit dans le bloc itérateur, et ses fonctions les plus essentielles sont au nombre de deux : l'une est de fournir des valeurs à l'objet d'énumération "séquentiellement", et l'autre est de signaler la fin de l'itération. Les instructions correspondant à ces deux fonctions sont respectivement rendement rendement et rendement rupture.
Il y a deux petits exemples ci-dessous, un sans rendement et un avec rendement. Regardons d'abord le premier. Lors du débogage, il était évident que lorsque la méthode GetResult() était exécutée, elle sautait à l'intérieur de la méthode et terminait l'exécution, puis exécutait l'instruction de valeur actuelle de sortie. Les résultats montrent que le premier est 0, ce qui signifie que la position du numéro d'énumération renvoyé est 0 dans la collection, puis les données de parcours que je veux, c'est-à-dire que le numéro d'énumération ne changera pas tant que MoveNext() est appelé. Continuez à avancer pour obtenir la valeur suivante, mais maintenant toutes les données ont été chargées en mémoire.
Regardons à nouveau le deuxième exemple. Lorsque j'ai débogué la méthode GetResultByYield(), je voulais entrer dans cette méthode, cependant, j'ai constaté que j'avais directement exécuté la phrase suivante et que je n'avais pas entré GetResultByYield. () du tout. À ce moment-là, il a été constaté que result.Current était nul et que le code à l'intérieur de la méthode n'était pas exécuté du tout, j'ai donc deviné que la collection était vide à ce moment-là. Continuez le débogage, exécutez GetResultByYield() lorsque la méthode MoveNext() est exécutée, puis exécutez le return return, puis revenez à la méthode main() pour afficher la valeur dans l'ensemble représenté par l'énumérateur.
Comme vous pouvez le voir ci-dessus, ce n'est que lorsque l'appel de MoveNext() est nécessaire que la méthode est exécutée pour obtenir le résultat. Lorsqu'elle n'est pas utilisée, il n'y aura aucun résultat. Ici, le compilateur aura une machine à états pour enregistrer l'état de l'itérateur afin de garantir que la boucle for continue son exécution à partir de l'état où le dernier retour de rendement s'est arrêté. Ce processus est comme si Xiao Fang voulait boire un litre d'eau. S'il boit dans une tasse d'un litre, il doit alors préparer un récipient d'un litre et le remplir d'un litre d'eau au distributeur d'eau.
Si Xiao Fang boit la moitié de la boisson et ne peut pas la finir, le reste de l'eau sera recyclé, donc un litre d'eau doit être préparé, qu'il puisse ou non la finir, tout comme le premier exemple. Laissez maintenant le volume de la tasse diminuer à 0,2 litre. Après que Xiaofang ait bu une tasse, il se rend au distributeur d'eau pour chercher de l'eau, ne buvant que 0,2 litre à chaque fois. De cette façon, il n'ira chercher de l'eau que lorsqu'il voudra boire. S'il ne veut pas boire en train de boire, cela gaspillera évidemment plus d'eau que la première méthode, qui est comme le deuxième exemple. Enfin, selon les conditions, vous pouvez appeler Yield Return pour sortir de la boucle while lorsque les données ne sont plus nécessaires. Si vous n'écrivez pas Yield Break, vous pouvez toujours terminer l'itération normalement.
/// /// 不使用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; } } }
Les résultats de sortie sont les suivants :
2 . Rendement approfondi
Mettez le deuxième exemple ci-dessus dans l'outil Reflector et vous obtiendrez les trois morceaux de code suivants. Le premier paragraphe est le code C# de la classe Pragrom complète, le deuxième paragraphe est le code d'extension C# de la classe scellée d__0 et le troisième paragraphe est le code IL de la méthode GetResultByYield(). Dans le premier morceau de code, vous pouvez voir que le système génère automatiquement une classe scellée d__0, qui déclare certains champs avec des noms étranges, mais nous pouvons clairement voir que cette classe contient la méthode MoveNext() et la propriété Current les plus importantes.
Le deuxième morceau de code est le code d'extension C# de cette classe scellée. Je me demande si les lecteurs se posent la même question que moi à ce stade : pourquoi une classe scellée devrait-elle être automatiquement générée ? La réponse réside dans le troisième morceau de code. Vous pouvez voir que le tableau n'est pas parcouru dans la méthode GetResultByYield(), et vous ne voyez même pas l'instruction newarr pour créer le tableau. Au lieu de cela, newobj crée un objet instance de d__0. classe scellée. C'est pourquoi la méthode GetResultByYield() n'a pas été saisie du tout lors du débogage, car le véritable code d'implémentation se trouve dans la méthode MoveNext() dans la classe scellée. Comme mentionné précédemment, le rendement est pris à la demande, une machine d'état est donc nécessaire pour enregistrer l'état de chaque retour de rendement.
在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)!