Home  >  Article  >  Backend Development  >  C# basics yield and Singleton

C# basics yield and Singleton

黄舟
黄舟Original
2017-02-07 17:03:361012browse

1. Example analysis of the role of yield

I recently saw the yield keyword for the first time in a Java written test. Since I have met it, I must master it. The following is a summary of the yield keyword in C#. The yield keyword acts in the iterator block, and its most essential functions are two: one is to provide values ​​to the enumeration object "sequentially", and the other is to signal the end of the iteration. The statements corresponding to these two functions are yield return and yield break respectively.

There are two small examples below, one without yield and one with yield. Let’s look at the first one first. When I was debugging, it was obvious that when the GetResult() method was executed, it would jump to the inside of the method and complete the execution, and then execute the output current value statement. It can be seen from the results that the first one is 0, which means that the position of the returned enumeration number is 0 in the collection, and then the traversal data I want, that is to say, the enumeration number will only change after calling MoveNext(). Keep moving forward to get the next value, but now the data has all been loaded into memory.

Let’s look at the second example again. When I debugged the GetResultByYield() method, I wanted to enter the inside of this method. However, I found that I directly executed the next sentence and did not enter the GetResultByYield() method at all. At this time, it was found that result.Current was null, and the code inside the method was not executed at all, so I guessed that the collection was empty at this time. Continue debugging, execute GetResultByYield() when the MoveNext() method is executed, and then execute the yield return and then return to the main() method to output the value in the set represented by the enumerator.

You can see from the above that only when calling MoveNext() is needed, the method will be executed to obtain the results. When it is not used, there will be no results. Here the compiler will have a state machine to save the state of the iterator to ensure that the for loop continues execution from the state where the last yield return stopped. This process is like Xiao Fang wanting to drink one liter of water. If he drinks from a one-liter cup, then he has to prepare a one-liter container and fill it with one liter of water at the water dispenser.

If Xiaofang drinks half of the drink and cannot finish it, the remaining water will be recycled, so that one liter of water must be prepared regardless of whether he can finish it, just like the first example. Now let the volume of the cup shrink to 0.2 liters. After Xiaofang drinks a cup, he goes to the water dispenser to get water, drinking only 0.2 liters each time. In this way, he will only fetch water when he wants to drink. If he drinks halfway and doesn't want to drink, it will obviously waste more water than the first method. This is just like the second example. Finally, according to the conditions, the data is no longer needed and the yield return can be called to jump out of the while loop. If the yield break is not written, the iteration can still end normally.

///
    /// 不使用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;
            }
        }
 
    }

The output results are as follows:

C# basics yield and Singleton

C# basics yield and Singleton


## 2. In-depth yield

Put the second example above into the Reflector tool, and you will get the following three pieces of code. The first paragraph is the C# code of the complete Pragrom class, the second paragraph is the C# expansion code of the d__0 sealed class, and the third paragraph is the IL code of the GetResultByYield() method. In the first piece of code, you can see that the system automatically generates a d__0 sealed class, which declares some fields with strange names, but we can clearly see that this class contains the most important MoveNext() method and Current Attributes.

The second piece of code is the C# expansion code of this sealed class. I wonder if readers have the same question as me at this point: Why should a sealed class be automatically generated? The answer lies in the third piece of code. You can see that the array is not traversed in the GetResultByYield() method, and you do not even see the newarr instruction to create the array. Instead, newobj creates an instance object of the d__0 sealed class. This is why the GetResultByYield() method was not entered at all during debugging, because the real implementation code is in the MoveNext() method in the sealed class. As mentioned earlier, yield is taken on demand, so a state machine is needed to record the status of each 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的内容,更多相关内容请关注PHP中文网(www.php.cn)!


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Previous article:C# understands genericsNext article:C# understands generics