C#中的using和yield return混合使用

      最近寫代碼爲了爲了省事兒用了幾個yield return,由於我不想New一個List<T>或者T[]對象再往裏放元素,就直接返回IEnumerable<T>了。個人代碼裏還有不少須要Dispose的對象,因此又用了幾個using。寫着寫着我有點心虛了——這樣混合使用靠譜嗎?ide

     今天我花時間研究一下,並在這裏做個筆記,跟你們分享。筆者水平有限,有哪些理解錯誤或作的不到位的地方,還請各位專家點撥。this

     這是我寫的方法,循環外面一個using,整個方法裏代碼執行後釋放一個對象。循環裏面又一個using, 每次循環yield return後要釋放一個對象。那是否是任何狀況下這些[被建立了的須要釋放的]DisposableObject對象最後都會被釋放呢?spa

        private static IEnumerable<int> GetNumbers(int count)
        {
            using (DisposableObject parentDisposableObject = new DisposableObject("ParentDisposableObject"))
            {
                foreach (int number in Enumerable.Range(1, count))
                {
                    using (DisposableObject childDisposableObject = new DisposableObject(string.Format("ChildDisposableObject{0}", number)))
                    {
                        //if (number == 4)
                        //{
                        //    throw new Exception("異常。");
                        //}
                        if (number != 2)
                        {
                            yield return number * 10;
                        }
                        else
                        {
                            Console.WriteLine(" 循環{0} else 代碼執行了", number.ToString());
                        }
                        Console.WriteLine(" 循環{0}else下面的代碼執行了", number.ToString());
                    }
                }
            }
        }        
    }

 

     須要釋放資源的類定義以下,建立對象和釋放時都有輸出。code

class DisposableObject : IDisposable
    {
        private string _value;
        public DisposableObject(string value)
        {
            _value = value;
            Console.WriteLine("Create Object {0}", _value);
        }
        public void Dispose()
        {
            Console.WriteLine("Disposable Object {0}", _value);
        }
    }

    這裏調用下:orm

        static void Main(string[] args)
        {
            foreach (int number in GetNumbers(5))
            {
                Console.WriteLine("結果 {0}", number.ToString());
            }
        }

 

    看看運行結果:對象

    

     咱們能夠看到:一、循環外面的對象和循環裏面的DisposableObject對象都被釋放了,這個讓我很高興,要的就是這個效果;2,若是yield return後面還有代碼,[yield] return後還會繼續執行;3,if-else有做用,不知足條件能夠不把該項做爲結果返回,不想執行某段代碼能夠放{}裏。這個運行的結果我很滿意,就是我想要的!blog

     下面我把拋異常的代碼註釋去掉,看看循環內拋出的異常後可否正常釋放對象。接口

     結果很完美,擔心是多餘的,該釋放的DisposableObject對象都被釋放了!資源

     那麼咱們簡單研究下yield return吧,我寫了下面最簡單的代碼:get

        
        private static IEnumerable<int> GetNumbers(int[] numbers)
        {
            foreach (int number in numbers)
            {
                yield return number*10;
            }
        }        

      把項目編譯再反編譯成C#2.0,發現代碼變成了這個樣子:

private static IEnumerable<int> GetNumbers(int[] numbers)
{
    <GetNumbers>d__0 d__ = new <GetNumbers>d__0(-2);     d__.<>3__numbers = numbers;
    return d__;
}

 

 


     這裏的<GetNumbers>d__0是個自動生成的類(看來這是高熱量的語法糖,吃的是少了,程序集卻發胖了!),它實現了IEnumerable<T>,IEnumerator<T>等接口,而上面方法其實就是返回了一個封裝了迭代器塊代碼的計數對象而已,若是您僅僅調用了一下上面這個方法,它可能不會執行循環中的代碼,除非觸發了返回值的MoveNext方法,這就是傳說中的延遲求值吧!

[CompilerGenerated]
private sealed class <GetNumbers>d__0 : IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable
{
    // Fields
    private int <>1__state;
    private int <>2__current;
    public int[] <>3__numbers;
    public int[] <>7__wrap3;
    public int <>7__wrap4;
    private int <>l__initialThreadId;
    public int <number>5__1;
    public int[] numbers;

    // Methods
    [DebuggerHidden]
    public <GetNumbers>d__0(int <>1__state);
    private void <>m__Finally2();
    private bool MoveNext();
    [DebuggerHidden]
    IEnumerator<int> IEnumerable<int>.GetEnumerator();
    [DebuggerHidden]
    IEnumerator IEnumerable.GetEnumerator();
    [DebuggerHidden]
    void IEnumerator.Reset();
    void IDisposable.Dispose();

    // Properties
    int IEnumerator<int>.Current { [DebuggerHidden] get; }
    object IEnumerator.Current { [DebuggerHidden] get; }
}

 
Expand Methods
 
View Code

     經過MSIL查看上面的foreach循環會調用MoveNext方法。

entrypoint
    .maxstack 2
    .locals init (
        [0] int32 number,
        [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)
    L_0000: ldc.i4.5 
    L_0001: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> ConsoleApplication1.Program::GetNumbers(int32)
    L_0006: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    L_000b: stloc.1 
    L_000c: br.s L_0026
    L_000e: ldloc.1 
    L_000f: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()
    L_0014: stloc.0 
    L_0015: ldstr "\u7ed3\u679c\uff1a{0}"
    L_001a: ldloca.s number
    L_001c: call instance string [mscorlib]System.Int32::ToString()
    L_0021: call void [mscorlib]System.Console::WriteLine(string, object)
    L_0026: ldloc.1 
    L_0027: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
    L_002c: brtrue.s L_000e
    L_002e: leave.s L_003a
    L_0030: ldloc.1 
    L_0031: brfalse.s L_0039
    L_0033: ldloc.1 
    L_0034: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0039: endfinally 
    L_003a: ret 
    .try L_000c to L_0030 finally handler L_0030 to L_003a
View Code

    而循環裏面的執行內容都在MoveNext方法裏。

private bool MoveNext()
{
    try
    {
        switch (this.<>1__state)
        {
            case 0:
                this.<>1__state = -1;
                this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject");
                this.<>1__state = 1;
                this.<>7__wrap5 = Enumerable.Range(1, this.count).GetEnumerator();
                this.<>1__state = 2;
                while (this.<>7__wrap5.MoveNext())
                {
                    this.<number>5__2 = this.<>7__wrap5.Current;
                    this.<childDisposableObject>5__3 = new DisposableObject(string.Format("ChildDisposableObject{0}", this.<number>5__2));
                    this.<>1__state = 3;
                    if (this.<number>5__2 == 4)
                    {
                        throw new Exception("異常。");
                    }
                    if (this.<number>5__2 == 2)
                    {
                        goto Label_00D0;
                    }
                    this.<>2__current = this.<number>5__2 * 10;
                    this.<>1__state = 4;
                    return true;
                Label_00C7:
                    this.<>1__state = 3;
                    goto Label_00E8;
                Label_00D0:
                    Console.WriteLine("循環{0}:else 內代碼執行了 ", this.<number>5__2.ToString());
                Label_00E8:
                    Console.WriteLine("循環{0}:else下面的代碼執行了 ", this.<number>5__2.ToString());
                    this.<>m__Finally7();
                }
                this.<>m__Finally6();
                this.<>m__Finally4();
                break;

            case 4:
                goto Label_00C7;
        }
        return false;
    }
    fault
    {
        this.System.IDisposable.Dispose();
    }
}

 

 
View Code

     接着再看下using,也來個最簡單的。

            using (DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject"))
            {
                Console.WriteLine("執行...");
                //throw new Exception("異常。");
            }

      而後咱們看一下對應的MSIL:

    .entrypoint
    .maxstack 1
    .locals init (
        [0] class ConsoleApplication1.DisposableObject parentDisposableObject)
    L_0000: ldstr "MainDisposableObject"
    L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
    L_000a: stloc.0 
    L_000b: ldstr "\u6267\u884c..."
    L_0010: call void [mscorlib]System.Console::WriteLine(string)
    L_0015: leave.s L_0021
    L_0017: ldloc.0 
    L_0018: brfalse.s L_0020
    L_001a: ldloc.0 
    L_001b: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0020: endfinally 
    L_0021: ret 
    .try L_000b to L_0017 finally handler L_0017 to L_0021

     再換一種C#寫法試試:

            DisposableObject parentDisposableObject = new DisposableObject("MainDisposableObject");
            try
            {
                Console.WriteLine("執行...");
                //throw new Exception("異常。");
            }
            finally
            {
                parentDisposableObject.Dispose();
            }

     對應的MSIL代碼:

    .entrypoint
    .maxstack 1
    .locals init (
        [0] class ConsoleApplication1.DisposableObject parentDisposableObject)
    L_0000: ldstr "MainDisposableObject"
    L_0005: newobj instance void ConsoleApplication1.DisposableObject::.ctor(string)
    L_000a: stloc.0 
    L_000b: ldstr "\u6267\u884c..."
    L_0010: call void [mscorlib]System.Console::WriteLine(string)
    L_0015: leave.s L_001e
    L_0017: ldloc.0 
    L_0018: callvirt instance void ConsoleApplication1.DisposableObject::Dispose()
    L_001d: endfinally 
    L_001e: ret 
    .try L_000b to L_0017 finally handler L_0017 to L_001e

     看看兩段MSIL多像啊,特別是最後一句!

     最後咱們看看yield return 和 using混合使用時,自動生成的<GetNumbers>d__0類是如何保證須要釋放資源的DisposableObject對象被釋放的,看後我不由感慨:C#的編譯器真是鬼斧神工啊!

 1 private bool MoveNext()
 2 {
 3     try
 4     {
 5         switch (this.<>1__state)
 6         {
 7             case 0:
 8                 this.<>1__state = -1;
 9                 this.<parentDisposableObject>5__1 = new DisposableObject("ParentDisposableObject"); 10                 this.<>1__state = 1;
11                 this.<>7__wrap5 = Enumerable.Range(1, this.count).GetEnumerator();
12                 this.<>1__state = 2;
13                 while (this.<>7__wrap5.MoveNext())
14                 {
15                     this.<number>5__2 = this.<>7__wrap5.Current;
16                     this.<childDisposableObject>5__3 = new DisposableObject(string.Format("ChildDisposableObject{0}", this.<number>5__2)); 17                     this.<>1__state = 3;
18                     if (this.<number>5__2 == 4)
19                     {
20                         throw new Exception("異常。");
21                     }
22                     if (this.<number>5__2 == 2)
23                     {
24                         goto Label_00D0;
25                     }
26                     this.<>2__current = this.<number>5__2 * 10;
27                     this.<>1__state = 4;
28                     return true;
29                 Label_00C7:
30                     this.<>1__state = 3;
31                     goto Label_00E8;
32                 Label_00D0:
33                     Console.WriteLine("循環{0}:else 內代碼執行了 ", this.<number>5__2.ToString());
34                 Label_00E8:
35                     Console.WriteLine("循環{0}:else下面的代碼執行了 ", this.<number>5__2.ToString());
36                     this.<>m__Finally7(); 37                 }
38                 this.<>m__Finally6();
39                 this.<>m__Finally4(); 40                 break;
41 
42             case 4:
43                 goto Label_00C7;
44         }
45         return false;
46     }
47     fault
48     {
49         this.System.IDisposable.Dispose(); 50     }
51 }
52 
53  
54 
55  
 1 public DisposableObject <parentDisposableObject>5__1;
 2  
 3 
 4 public DisposableObject <childDisposableObject>5__3;
 5 
 6 
 7 private void <>m__Finally4()
 8 {
 9     this.<>1__state = -1;
10     if (this.<parentDisposableObject>5__1 != null)
11     {
12         this.<parentDisposableObject>5__1.Dispose();
13     }
14 }
15 
16 private void <>m__Finally7()
17 {
18     this.<>1__state = 2;
19     if (this.<childDisposableObject>5__3 != null)
20     {
21         this.<childDisposableObject>5__3.Dispose();
22     }
23 }
24 
25 void IDisposable.Dispose()
26 {
27     switch (this.<>1__state)
28     {
29         case 1:
30         case 2:
31         case 3:
32         case 4:
33             try
34             {
35                 switch (this.<>1__state)
36                 {
37                     case 2:
38                     case 3:
39                     case 4:
40                         try
41                         {
42                             switch (this.<>1__state)
43                             {
44                                 case 3:
45                                 case 4:
46                                     try
47                                     {
48                                     }
49                                     finally
50                                     {
51                                         this.<>m__Finally7();
52                                     }
53                                     break;
54                             }
55                         }
56                         finally
57                         {
58                             this.<>m__Finally6();
59                         }
60                         break;
61                 }
62             }
63             finally
64             {
65                 this.<>m__Finally4();
66             }
67             break;
68 
69         default:
70             return;
71     }
72 }
相關文章
相關標籤/搜索