余小章 @ 大內聖殿
祕訣無它, 唯勤而已, by 余小章

导航

 

我們都知道using可以釋放實作IDisposable介面的類別,主要是釋放非托管的物件,在操作檔案的時候就必須要釋放才不會造成檔案鎖定,[.NET] 使用 using 或 try/finally 清理資源,使用

try/finally的寫法如下

public string ReaderLine(string FileName)
{
    if (!File.Exists(FileName))
        return null;
    Stream stream = null;
    StreamReader reader = null;
    string line = "";
    StringBuilder sb = new StringBuilder();
    try
    {
        stream = File.Open(FileName, FileMode.Open);
        reader = new StreamReader(stream);
        while ((line = reader.ReadLine()) != null)
        {
            sb.AppendLine(line);
        }

        return sb.ToString();
    }
    finally
    {
        if (stream != null)
            stream.Close();
        if (reader != null)
            reader.Close();
    }
}

 

使用using的寫法如下

public string ReaderLine(string FileName)
{
    if (!File.Exists(FileName))
        return null;

    string line = "";
    StringBuilder sb = new StringBuilder();
    using (Stream stream = File.Open(FileName, FileMode.Open))
    {
        using (StreamReader reader = new StreamReader(stream))
        {
            while ((line = reader.ReadLine()) != null)
            {
                sb.AppendLine(line);
            }
            return sb.ToString();
        }
    }
}


上述的寫法例外發生時會被吃掉嗎?

我相信很多人對上述的寫法會有疑問,我們來做個小實驗,我在try區塊以及using區塊裡面故意寫轉型失敗的程式碼,我們可以發現VS仍有拋出例外訊息。

image

image

所以不管是using還是try/finally都不會吃掉例外訊息。


using和try/finally有什麼不同?

基本上它們兩個是一樣的東西,先來用Reflector看看using所產生的IL,using區段變成try/finlly了

image

.method public hidebysig instance string ReaderLine(string FileName) cil managed
{
    .maxstack 2
    .locals init (
        [0] string line,
        [1] class [mscorlib]System.Text.StringBuilder sb,
        [2] class [mscorlib]System.IO.Stream stream,
        [3] class [mscorlib]System.IO.StreamReader reader,
        [4] string a,
        [5] uint8 b,
        [6] string CS$1$0000,
        [7] bool CS$4$0001)
    L_0000: nop 
    L_0001: ldarg.1 
    L_0002: call bool [mscorlib]System.IO.File::Exists(string)
    L_0007: stloc.s CS$4$0001
    L_0009: ldloc.s CS$4$0001
    L_000b: brtrue.s L_0012
    L_000d: ldnull 
    L_000e: stloc.s CS$1$0000
    L_0010: br.s L_008d
    L_0012: ldstr ""
    L_0017: stloc.0 
    L_0018: newobj instance void [mscorlib]System.Text.StringBuilder::.ctor()
    L_001d: stloc.1 
    L_001e: ldarg.1 
    L_001f: ldc.i4.3 
    L_0020: call class [mscorlib]System.IO.FileStream [mscorlib]System.IO.File::Open(string, valuetype [mscorlib]System.IO.FileMode)
    L_0025: stloc.2 
    L_0026: nop 
    L_0027: ldloc.2 
    L_0028: newobj instance void [mscorlib]System.IO.StreamReader::.ctor(class [mscorlib]System.IO.Stream)
    L_002d: stloc.3 
    L_002e: nop 
    L_002f: br.s L_003b
    L_0031: nop 
    L_0032: ldloc.1 
    L_0033: ldloc.0 
    L_0034: callvirt instance class [mscorlib]System.Text.StringBuilder [mscorlib]System.Text.StringBuilder::AppendLine(string)
    L_0039: pop 
    L_003a: nop 
    L_003b: ldloc.3 
    L_003c: callvirt instance string [mscorlib]System.IO.TextReader::ReadLine()
    L_0041: dup 
    L_0042: stloc.0 
    L_0043: ldnull 
    L_0044: ceq 
    L_0046: ldc.i4.0 
    L_0047: ceq 
    L_0049: stloc.s CS$4$0001
    L_004b: ldloc.s CS$4$0001
    L_004d: brtrue.s L_0031
    L_004f: ldstr "999"
    L_0054: stloc.s a
    L_0056: ldloc.s a
    L_0058: call uint8 [mscorlib]System.Byte::Parse(string)
    L_005d: stloc.s b
    L_005f: ldloc.1 
    L_0060: callvirt instance string [mscorlib]System.Object::ToString()
    L_0065: stloc.s CS$1$0000
    L_0067: leave.s L_008d
    L_0069: ldloc.3 
    L_006a: ldnull 
    L_006b: ceq 
    L_006d: stloc.s CS$4$0001
    L_006f: ldloc.s CS$4$0001
    L_0071: brtrue.s L_007a
    L_0073: ldloc.3 
    L_0074: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_0079: nop 
    L_007a: endfinally 
    L_007b: ldloc.2 
    L_007c: ldnull 
    L_007d: ceq 
    L_007f: stloc.s CS$4$0001
    L_0081: ldloc.s CS$4$0001
    L_0083: brtrue.s L_008c
    L_0085: ldloc.2 
    L_0086: callvirt instance void [mscorlib]System.IDisposable::Dispose()
    L_008b: nop 
    L_008c: endfinally 
    L_008d: nop 
    L_008e: ldloc.s CS$1$0000
    L_0090: ret 
    .try L_002e to L_0069 finally handler L_0069 to L_007b
    .try L_0026 to L_007b finally handler L_007b to L_008d
}

 

再看看Reflector不優化IL的結果,選C# None,Reflector會幫我們直接翻譯IL成C#,當然你還可以翻成其它.NET語言

image

public string ReaderLine(string FileName)
{
    string line;
    StringBuilder sb;
    Stream stream;
    StreamReader reader;
    string a;
    byte b;
    string CS$1$0000;
    bool CS$4$0001;
    if (File.Exists(FileName) != null)
    {
        goto Label_0012;
    }
    CS$1$0000 = null;
    goto Label_008D;
Label_0012:
    line = "";
    sb = new StringBuilder();
    stream = File.Open(FileName, 3);
Label_0026:
    try
    {
        reader = new StreamReader(stream);
    Label_002E:
        try
        {
            goto Label_003B;
        Label_0031:
            sb.AppendLine(line);
        Label_003B:
            if ((((line = reader.ReadLine()) == null) == 0) != null)
            {
                goto Label_0031;
            }
            a = "999";
            b = byte.Parse(a);
            CS$1$0000 = sb.ToString();
            goto Label_008D;
        }
        finally
        {
        Label_0069:
            if ((reader == null) != null)
            {
                goto Label_007A;
            }
            reader.Dispose();
        Label_007A:;
        }
    }
    finally
    {
    Label_007B:
        if ((stream == null) != null)
        {
            goto Label_008C;
        }
        stream.Dispose();
    Label_008C:;
    }
Label_008D:
    return CS$1$0000;
}


後記:

由上面的演示我們可以知道

1.using跟try/finally,都不會吃掉例外訊息,因為沒有寫catch區段。

2.using是try/finally的簡易寫法。

兩者的寫法結果都一樣,就看自己喜歡什麼樣子的寫法,using的寫法看起來比較短,若只有一層我會使用,但如果有多層using(兩層以上),我就會覺得閱讀上有很大的不便,我就不會採用這樣的寫法。

posted on 2012-01-03 12:12  余小章  阅读(345)  评论(0编辑  收藏  举报