Using() Statement in 3 seconds and a bug in Reflector

The boring, known accross the board definition from the MSDN site: Defines a scope, outside of which an object or objects will be disposed

The more interesting definition from Tom: The using() Statement generates a try{} finally{ //virtual call of the dispose method } block with null check!

Let's take a deeper look.

Let's write two test methods first:

//Testmethod for Using
public static void UsingTest()
{
using (SqlConnection con = new SqlConnection())
{
con.ConnectionString = "Test Connection";
}
} //TestMethod for try finally
public static void TryFinallytest()
{
SqlConnection con = new SqlConnection();
try
{
con.ConnectionString = "Test Connection";
}
finally
{
con.Dispose();
}
}

Note: The TryFinallyTest() method has no null check inside the finally block. This means in case con is null the function would throw a Null Reference Exception. The UsingTest() method does not.

When you compile both methods and look at them disambeled in Reflector they both look exatly the same:

public static void UsingTest()
{
using (SqlConnection con = new SqlConnection())
{
con.ConnectionString = "Test Connection";
}
} public static void TryFinallytest()
{
using (SqlConnection con = new SqlConnection())
{
con.ConnectionString = "Test Connection";
}
}

But looking at the IL (see below) reveals a very important difference: -  Frist both get translated into a try{} finally {} block. (see IL code below). This is what we expected -  The using(){} statement adds a null check before calling the dispose method inside the finally. -  The compiler did not add a null check for the TryFinallyTest method (as intented)

The Problem: The TryFinallyTest method gets disasembeld from IL into C# (or VB) by using a using() statement . This is not correct because a using() statement has a null check before calling the dispose() method. This means the disasembled C# code does not relfect the IL correctly.

Here is the IL code for the UsingTest() method:

.method public hidebysig static void UsingTest() cil managed
{
.maxstack 2
.locals init (
[0] class [System.Data]System.Data.SqlClient.SqlConnection con,
[1] bool CS$4$0000)
L_0000: nop
L_0001: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor()
L_0006: stloc.0
L_0007: nop
L_0008: ldloc.0
L_0009: ldstr "Test Connection"
L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::set_ConnectionString(string)
L_0013: nop
L_0014: nop
L_0015: leave.s L_0027
L_0017: ldloc.0
L_0018: ldnull
L_0019: ceq
L_001b: stloc.1
L_001c: ldloc.1
L_001d: brtrue.s L_0026 //Nullcheck
L_001f: ldloc.0
L_0020: callvirt instance void [mscorlib]System.IDisposable::Dispose() //dispose call
L_0025: nop
L_0026: endfinally
L_0027: nop
L_0028: ret
.try L_0007 to L_0017 finally handler L_0017 to L_0027
}

Note: L_001d: brtrue.s perfoms the null check. In case con is null it jumps directly to the endfinally. This means in case con is null there is no Null Reference Exception

Here is the IL code for the TryFinallyTest method:

.method public hidebysig static void TryFinallytest() cil managed
{
.maxstack 2
.locals init (
[0] class [System.Data]System.Data.SqlClient.SqlConnection con)
L_0000: nop
L_0001: newobj instance void [System.Data]System.Data.SqlClient.SqlConnection::.ctor()
L_0006: stloc.0
L_0007: nop
L_0008: ldloc.0
L_0009: ldstr "Test Connection"
L_000e: callvirt instance void [System.Data]System.Data.Common.DbConnection::set_ConnectionString(string)
L_0013: nop
L_0014: nop
L_0015: leave.s L_0021
L_0017: nop
L_0018: ldloc.0 //it loads con and calls directly dispose in the next line. No null check
L_0019: callvirt instance void [System]System.ComponentModel.Component::Dispose()
L_001e: nop
L_001f: nop
L_0020: endfinally
L_0021: nop
L_0022: ret
.try L_0007 to L_0017 finally handler L_0017 to L_0021
}

Note: Inside the the finally block the IL loads con on the Excution Stack and calls dispose() directly without a null check. In case con is null, an Null Reference Exception is thrown.

So, what to use? Using() or try{} finally{}? Well, usually I would recommend using Using(), so you don't have to remember the null check. But if you have multiple using inside eachother I would rather use one try{} finally{}.

If you don't know (or if you are too lazy to check)  if your object implements IDisposable you can always use the following pattern to be on the save side:

public static void ObjectAsDispose(SqlConnection con)
{
using (con as IDisposable)
{
con.ConnectionString =
"Test Connection";
}

}

I hope you enjoyed this one again.

Tom

P.S.: Thanks Ian for reminding me to add the safty pattern for using!

[转]C# and the using Statement in 3 seconds and a bug in Reflector的更多相关文章

  1. Oracle 课程八之性能优化之10053事件

    一. 10053事件 当一个SQL出现性能问题的时候,可以使用SQL_TRACE 或者 10046事件来跟踪SQL. 通过生成的trace来了解SQL的执行过程. 我们在查看一条SQL的执行计划的时候 ...

  2. 13.1.17 CREATE TABLE Syntax

    13.1.17 CREATE TABLE Syntax 13.1.17.1 CREATE TABLE ... LIKE Syntax 13.1.17.2 CREATE TABLE ... SELECT ...

  3. 针对数据泵导出 (expdp) 和导入 (impdp)工具性能降低问题的检查表 (文档 ID 1549185.1)

    针对数据泵导出 (expdp) 和导入 (impdp)工具性能降低问题的检查表 (文档 ID 1549185.1) 文档内容 适用于: Oracle Database – Enterprise Edi ...

  4. org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): da.huying.usermanag ...

  5. [解决方案]CREATE DATABASE statement not allowed within multi-statement transaction.

    CREATE DATABASE statement not allowed within multi-statement transaction. 刚开始报这个错误的时候,我上度娘搜了一下. 别人是在 ...

  6. Drop all the tables, stored procedures, triggers, constraints and all the dependencies in one SQL statement

    Is there any way in which I can clean a database in SQl Server 2005 by dropping all the tables and d ...

  7. JDBC中的Statement和PreparedStatement的区别

    JDBC中的Statement和PreparedStatement的区别  

  8. SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问

    delphi ado 跨数据库访问 语句如下 ' and db = '帐套1' 报错内容是:SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATE ...

  9. Exception:HTTP Status 500 - org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)

    主要错误信息如下: HTTP Status 500 - org.apache.ibatis.binding.BindingException: Invalid bound statement (not ...

随机推荐

  1. JAVA深入研究——Method的Invoke方法(转)

    在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java ...

  2. 手机号ID开关星号(*)

    .h文件 <span style="font-size:18px;">/** * 转成星号工具 */ @interface AsteriskTool : NSObjec ...

  3. Find a way (BFS)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2612 BFS搜索  目标地  并记录下来  之后再判断两段路程之和 代码: #include < ...

  4. win7或windows server 2008 R2 被远程登录日志记录 系统日志

    事件查看器 → Windows 日志 → 安全 (win7 事件查看器 打开方式 :计算机 右键   → 管理  → 计算机管理 → 系统工具 → 事件查看器 windows server 2008 ...

  5. React.js终探(七)(完)

    我们在前面介绍了组件的各种特性,这一节我们来说说多组件的情况. 在实际开发中,我们的组件难免会遇到有公共部分的情况,如果是个别情况还好,但如果数量比较多的话,那这时候,就需要公用了. 怎么公用呢? R ...

  6. javascript 中的location.pathname

    1 location.pathname; 这在之前我没怎么注意过,所以研究研究.location.pathname:返回URL的域名(域名IP)后的部分.例如 http://www.joymood.c ...

  7. Group and sum array of hashes by date

    I have an array of hashes like this: [{:created=>Fri, 22 Jan 2014 13:02:13 UTC +00:00, :amount=&g ...

  8. 【LeetCode】 sort list 单清单归并

    称号:Sort a linked list in O(n log n) time using constant space complexity. 思路:要求时间复杂度O(nlogn) 知识点:归并排 ...

  9. windows+php5.5+apache2.4+tomcat+mod_jk配置

    原因: 通常情况下apache执行的是80port,比方apache启动后执行localhost:80就能够出现It works页面,这里的80也能够不写,会默认的.而tomcat启动时默认的port ...

  10. JAVA简单Swing图形界面应用演示样例

    JAVA简单Swing图形界面应用演示样例 package org.rui.hello; import javax.swing.JFrame; /** * 简单的swing窗体 * @author l ...