用Using到底對DB那幾個元件做了什麼

用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.

 

參考 :

DataReader 的Close時機

DB Connection 的Close與Dispose