电竞比分网-中国电竞赛事及体育赛事平台

分享

C#基礎(chǔ)之yield與Singleton

 weijianian 2016-08-09


來源:方小白

鏈接:http://www.cnblogs.com/fangyz/p/5597195.html


1.實(shí)例解析yiled的作用


最近參加java筆試題第一次見到y(tǒng)ield這個(gè)關(guān)鍵字,既然遇見了那肯定要掌握,下面是C#中關(guān)于yield關(guān)鍵字的總結(jié)。yield這個(gè)關(guān)鍵字作用于迭代器塊中,其最本質(zhì)的功能有2個(gè):一是“依次”向枚舉對(duì)象提供值,二是發(fā)出迭代結(jié)束信號(hào)。這兩個(gè)功能對(duì)應(yīng)的語句分別是yield return和yield break。


下面有2個(gè)小例子,分別沒有使用yield和有使用yield。先來看第一個(gè),當(dāng)我調(diào)試時(shí)顯然執(zhí)行到GetResult()方法時(shí)將會(huì)跳轉(zhuǎn)到方法內(nèi)部并且執(zhí)行完,接著再去執(zhí)行輸出當(dāng)前值語句。從結(jié)果可以看出第一個(gè)是0,說明返回的枚舉數(shù)所在的位置在集合中是0,接著才是我想要的遍歷數(shù)據(jù),也就是說只有調(diào)用MoveNext()后枚舉數(shù)才會(huì)繼續(xù)向前移動(dòng)得到下一個(gè)值,但是此時(shí)數(shù)據(jù)已全部加載到內(nèi)存。


再來看第二個(gè)例子,當(dāng)我調(diào)試到GetResultByYield()方法時(shí)我想進(jìn)入到這個(gè)方法內(nèi)部結(jié)果發(fā)現(xiàn)直接執(zhí)行下一句,根本就沒有進(jìn)入到GetResultByYield()方法內(nèi)部。此時(shí)發(fā)現(xiàn)result.Current是null,再加上前面根本都沒執(zhí)行方法內(nèi)部的代碼,因此我猜測(cè)此時(shí)集合都是空的。繼續(xù)調(diào)試,當(dāng)執(zhí)行MoveNext()方法時(shí)才去執(zhí)行GetResultByYield(),接著執(zhí)行到y(tǒng)ield return隨即返回main()方法輸出枚舉數(shù)所代表的集合中的值。


從上面可以看到只有調(diào)用MoveNext()需要用的時(shí)候才去執(zhí)行方法來獲得結(jié)果,不用的時(shí)候并不會(huì)有任何結(jié)果。這個(gè)地方編譯器會(huì)有一個(gè)狀態(tài)機(jī)用來保存迭代器的狀態(tài),以保證for循環(huán)時(shí)是從上一次yield return停止的狀態(tài)繼續(xù)執(zhí)行。這個(gè)過程就好比小方要喝一升的水,如果它用一個(gè)一升的杯子喝那么他要準(zhǔn)備一個(gè)一升的容器去飲水機(jī)裝滿一升的水。


如果小方喝到一半喝不完了,那接下來剩下的水則將被回收,這樣無論能不能喝完都必須準(zhǔn)備好一升的水,就像第一個(gè)例子?,F(xiàn)在讓杯子的容積縮小為0.2升,小方喝完一杯后再去飲水機(jī)去打水,每次只喝0.2升。這樣只有他要去喝的時(shí)候才去打水,如果他喝到一半不想喝了顯然浪費(fèi)的水比第一種方式多,這就像第二個(gè)例子。最后根據(jù)條件不再需要數(shù)據(jù)便可調(diào)用yield return來跳出while循環(huán),如果不寫yield break仍然可以正常結(jié)束迭代。


///

    /// 不使用yield的時(shí)候

    ///

    class Program

    {

        static void Main(string[] args)

        {

            //得到一個(gè)迭代結(jié)果

            var result = GetResult();

            //輸出當(dāng)前的值

            Console.WriteLine(result.Current);

            Console.WriteLine('開始遍歷');

            while (result.MoveNext())

            {

                Console.WriteLine(result.Current);

            }

            Console.WriteLine('遍歷結(jié)束');

            Console.ReadLine();

        }

        //不使用yield來進(jìn)行迭代

        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關(guān)鍵字

    ///

    class Program

    {

        static void Main(string[] args)

        {

            //得到一個(gè)迭代結(jié)果

            var result = GetResultByYield();

            //輸出當(dāng)前的值

            Console.WriteLine(result.Current);

            Console.WriteLine('開始遍歷');

            while (result.MoveNext())

            {

                Console.WriteLine(result.Current);

            }

            Console.WriteLine('遍歷結(jié)束');

            Console.ReadLine();  

        }

        //使用yield來進(jìn)行迭代

        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;

            }

        }

 

    }


輸出結(jié)果如下:




2.深入yield


將上面第二個(gè)例子放入Reflector工具中,便得到了下面三段代碼。第一段是完整的Pragrom類的C#代碼,第二段是d__0密封類的C#展開代碼,第三段是GetResultByYield()方法的IL代碼。在第一段代碼中可以看到系統(tǒng)自動(dòng)生成了一個(gè)d__0密封類,它里面聲明了一些名字很奇怪的字段,不過我們可以很清楚的看到這個(gè)類里面有最重要的MoveNext()方法和Current屬性。


第二段代碼則是這個(gè)密封類的C#展開代碼,到這里不知道讀者有沒有和我當(dāng)初一樣的疑問:為什么要自動(dòng)生成一個(gè)密封類呢?答案就在第三段代碼中,可以看到在GetResultByYield()方法中并沒有遍歷數(shù)組,甚至都沒有看到創(chuàng)建數(shù)組的newarr指令,而是newobj創(chuàng)建了d__0密封類的實(shí)例對(duì)象。這也正是前面調(diào)試的時(shí)候?yàn)槭裁锤揪蜎]進(jìn)去GetResultByYield()方法的原因,因?yàn)檎嬲娴膶?shí)現(xiàn)代碼是在密封類里面的MoveNext()方法中。前面還提到y(tǒng)ield是按需所取,因此需要一個(gè)狀態(tài)機(jī)來記錄每次yield return的狀態(tài)。


在MoveNext()方法中由于密封類構(gòu)造函數(shù)傳進(jìn)去的是一個(gè)0(在第三段代碼中可以看到),因此第一次進(jìn)入到MoveNext方法時(shí)this.__state=0。此時(shí)current字段由于沒賦值因此就是null了。接著創(chuàng)建數(shù)組并開始一個(gè)while循環(huán)(原來foreach就是while循環(huán)),在循環(huán)中給current字段賦值并讓state字段值為2,最后返回true。拿Current屬性時(shí)就是拿while循環(huán)中給current賦的值,再次進(jìn)入這個(gè)方法內(nèi)此時(shí)state等于2于是跳轉(zhuǎn)到Label_0090,也就是進(jìn)入while語句塊中繼續(xù)循環(huán),這就是按需所取的原理。當(dāng)遇到y(tǒng)ield break后會(huì)先執(zhí)行Dispose釋放資源,再執(zhí)行break語句跳出循環(huán)??梢钥吹缴鲜鲞@個(gè)過程就是一個(gè)狀態(tài)機(jī),而這個(gè)密封類是為建立一個(gè)狀態(tài)機(jī)來生成的,現(xiàn)在我們自己也可以寫出一個(gè)狀態(tài)機(jī)了。


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.單例模式


單例模式?jīng)]什么好說的,當(dāng)然如果深挖應(yīng)該也是大有學(xué)問,其中我覺得比較好的一種寫法如下。單例模式的代碼我看過多次不過卻沒怎么寫,結(jié)果真真寫的時(shí)候再加上時(shí)間又有點(diǎn)緊最后寫的一塌糊涂。以后寫代碼要興平氣和地去寫,急躁的狀態(tài)寫不出什么好代碼。當(dāng)然總會(huì)有煩躁的時(shí)候,所以只能多寫代碼來讓自己寫出高質(zhì)量的代碼成為一種習(xí)慣!


 class A

    {

        private static A instance = new A();

        public static A Instance

        {

            get { return A.instance; }

        }

    }


    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多