用Using來包DBConnction,DBCommand,DBDataReader等,似乎是大家公認最安全的做法,只要離開了Using,它該關的就關,該丟的就丟,完全不需要我們去寫額外的Code去做,但離開Using時,它到底是做了什麼?它是用什麼樣的方式,確保離開Using就丟?
用Using來包DBConnction,DBCommand,DBDataReader等,似乎是大家公認最安全的做法,只要離開了Using,它該關的就關,該丟的就丟,完全不需要我們去寫額外的Code去做,但離開Using時,它到底是做了什麼?它是用什麼樣的方式,確保離開Using就丟?
先來看以下三種不同的Code.
private void Test1()
{
SqlConnection conn = new SqlConnection("");
SqlCommand cmd = conn.CreateCommand();
SqlDataReader dr = null;
try
{
//Do something
dr = cmd.ExecuteReader();
}
finally
{
dr.Close();
conn.Close();
}
}
private void Test2()
{
SqlConnection conn = new SqlConnection("");
SqlCommand cmd = conn.CreateCommand();
SqlDataReader dr = null;
try
{
//Do something
dr = cmd.ExecuteReader();
}
finally
{
dr.Dispose();
cmd.Dispose();
conn.Dispose();
}
}
private void Test3()
{
using (SqlConnection conn = new SqlConnection(""))
{
using (SqlCommand cmd = conn.CreateCommand())
{
//Do something
using (SqlDataReader dr = cmd.ExecuteReader())
{
}
}
}
}
Test1及Test2都是用Try-Finally的方式去包,而Test3則是使Using的方式去包,接下來把這三個function反組來看,看它起了什麼樣的變化.
Test1 :
.method private hidebysig instance void Test1() cil managed
{
// 程式碼大小 51 (0x33)
.maxstack 2
.locals init ([0] class [System.Data]System.Data.SqlClient.SqlConnection conn,
[1] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
[2] class [System.Data]System.Data.SqlClient.SqlDataReader dr)
IL_0000: nop
IL_0001: ldstr ""
IL_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: callvirt instance class [System.Data]System.Data.SqlClient.SqlCommand [System.Data]System.Data.SqlClient.SqlConnection::CreateCommand()
IL_0012: stloc.1
IL_0013: ldnull
IL_0014: stloc.2
.try
{
IL_0015: nop
IL_0016: ldloc.1
IL_0017: callvirt instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader()
IL_001c: stloc.2
IL_001d: nop
IL_001e: leave.s IL_0031
} // end .try
finally
{
IL_0020: nop
IL_0021: ldloc.2
IL_0022: callvirt instance void [System.Data]System.Data.Common.DbDataReader::Close()
IL_0027: nop
IL_0028: ldloc.0
IL_0029: callvirt instance void [System.Data]System.Data.Common.DbConnection::Close()
IL_002e: nop
IL_002f: nop
IL_0030: endfinally
} // end handler
IL_0031: nop
IL_0032: ret
} // end of method Form1::Test1
看起來沒什麼變化.finally裡還是Close
再來看看Test2
.method private hidebysig instance void Test2() cil managed
{
// 程式碼大小 58 (0x3a)
.maxstack 2
.locals init ([0] class [System.Data]System.Data.SqlClient.SqlConnection conn,
[1] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
[2] class [System.Data]System.Data.SqlClient.SqlDataReader dr)
IL_0000: nop
IL_0001: ldstr ""
IL_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: callvirt instance class [System.Data]System.Data.SqlClient.SqlCommand [System.Data]System.Data.SqlClient.SqlConnection::CreateCommand()
IL_0012: stloc.1
IL_0013: ldnull
IL_0014: stloc.2
.try
{
IL_0015: nop
IL_0016: ldloc.1
IL_0017: callvirt instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader()
IL_001c: stloc.2
IL_001d: nop
IL_001e: leave.s IL_0038
} // end .try
finally
{
IL_0020: nop
IL_0021: ldloc.2
IL_0022: callvirt instance void [System.Data]System.Data.Common.DbDataReader::Dispose()
IL_0027: nop
IL_0028: ldloc.1
IL_0029: callvirt instance void [System]System.ComponentModel.Component::Dispose()
IL_002e: nop
IL_002f: ldloc.0
IL_0030: callvirt instance void [System]System.ComponentModel.Component::Dispose()
IL_0035: nop
IL_0036: nop
IL_0037: endfinally
} // end handler
IL_0038: nop
IL_0039: ret
} // end of method Form1::Test2
一樣沒什麼變化,在finally還是Dispose
接下來就是Using了,它會是呼叫Close,還是Dispose,還是兩個都呼叫,還是有Close就呼叫它再呼叫Dispose,還是有"神奇"的東西出現?
它是怎麼確保離開Using就"丟"了它?
.method private hidebysig instance void Test3() cil managed
{
// 程式碼大小 90 (0x5a)
.maxstack 2
.locals init ([0] class [System.Data]System.Data.SqlClient.SqlConnection conn,
[1] class [System.Data]System.Data.SqlClient.SqlCommand cmd,
[2] class [System.Data]System.Data.SqlClient.SqlDataReader dr,
[3] bool CS$4$0000)
IL_0000: nop
IL_0001: ldstr ""
IL_0006: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor(string)
IL_000b: stloc.0
.try
{
IL_000c: nop
IL_000d: ldloc.0
IL_000e: callvirt instance class [System.Data]System.Data.SqlClient.SqlCommand [System.Data]System.Data.SqlClient.SqlConnection::CreateCommand()
IL_0013: stloc.1
.try
{
IL_0014: nop
IL_0015: ldloc.1
IL_0016: callvirt instance class [System.Data]System.Data.SqlClient.SqlDataReader [System.Data]System.Data.SqlClient.SqlCommand::ExecuteReader()
IL_001b: stloc.2
.try
{
IL_001c: nop
IL_001d: nop
IL_001e: leave.s IL_0030
} // end .try
finally
{
IL_0020: ldloc.2
IL_0021: ldnull
IL_0022: ceq
IL_0024: stloc.3
IL_0025: ldloc.3
IL_0026: brtrue.s IL_002f
IL_0028: ldloc.2
IL_0029: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_002e: nop
IL_002f: endfinally
} // end handler
IL_0030: nop
IL_0031: nop
IL_0032: leave.s IL_0044
} // end .try
finally
{
IL_0034: ldloc.1
IL_0035: ldnull
IL_0036: ceq
IL_0038: stloc.3
IL_0039: ldloc.3
IL_003a: brtrue.s IL_0043
IL_003c: ldloc.1
IL_003d: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0042: nop
IL_0043: endfinally
} // end handler
IL_0044: nop
IL_0045: nop
IL_0046: leave.s IL_0058
} // end .try
finally
{
IL_0048: ldloc.0
IL_0049: ldnull
IL_004a: ceq
IL_004c: stloc.3
IL_004d: ldloc.3
IL_004e: brtrue.s IL_0057
IL_0050: ldloc.0
IL_0051: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_0056: nop
IL_0057: endfinally
} // end handler
IL_0058: nop
IL_0059: ret
} // end of method Form1::Test3
答案是,它一樣是用try-finally的方式去包,而它所呼叫的一致是Dispose().
那個DBConnection及DBDataReader的Close呢?
因為Dispose本來就包含Close的動作,沒必要呼叫Close再來呼叫Dispose.
參考 :