[转]C# and the using Statement in 3 seconds and a bug in Reflector

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

时间: 2024-11-05 22:41:15

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

MySQL 5.7.15 发布,新增 InnoDB 配置选项

MySQL 5.7.15 发布了.更新如下: 安全事项: The validate_password plugin now supports the capability of rejecting passwords that match the current session user name, either forward or in reverse. To enable control over this capability, the plugin exposes a validate

Java——JDBC小结(4)

在前面的关于JDBC的叙述中,着重说明了工具类的创建办法,接下来我将就我之前没有提到的查询的方式加以说明,在说明查询之前我先引出一个新的概念就是结果集这个类,及他的相关的一些方法 1.结果集 之前有提到过发送SQL的Statement这个类,他的主要作用就是发送SQL到数据库,对于增,删,改,使用的方法是Statement.executeUpdata();注意他的返回值是一个整形,也就是返回的是数据库受影响的行数,而在进行查找的SQL时,要调用的方法是Statement.executeQuery

ajax交互servlet返回数据和jdbc模糊查询-中文-已经设置了UTF-8和解决了乱码

1.编码是将字符按一定翻译方式转换成字节存储在内存或外存中,解码是按照一定翻译方式将存储中的字节转换成字符. 2.ASCII是单字节,最高位总为0,相当于只占用了一个字节的7位,2^7=128个字符,相当于键盘上的128个键,有大小写因为字母,有*,%¥#@!+....等这些可显示字符,也有不可以显示的控制符F1,ctr... 而ISO-8859-1是启用了ASCII码的最高位,理论上是能再多128位,实际上没用掉这么多,至于多了那些,你们自己查下表 UTF-8是可变长的,具体中文几个字节,怎么

OGG 的最新版本Version 11.2.1.0.25 BLR 19358139– 1 September 2014

Version 11.2.1.0.25 BLR 19358139  fix了如下的bug: 19358139 - GoldenGate parsing multiple times. Cache the prepare update statement to checkpoint table. 19358139 是bug号:Bug 19358139 : GOLDENGATE PARSING MULTIPLE TIMES 该bug的描述如下: Hdr: 19358139 11.2.0.2 REPL

MySQL 5.7.15 发布,新增 InnoDB 和 CMake 选项

MySQL 5.7.15 发布了.更新如下: 安全事项: The validate_password plugin now supports the capability of rejecting passwords that match the current session user name, either forward or in reverse. To enable control over this capability, the plugin exposes a validate

Hibernate - HHH000352: Unable to release batch statement

这是hibernate的一个bug,具体看https://hibernate.atlassian.net/browse/HHH-11732?attachmentViewMode=list When using stateless session with jdbc batch size we get an HHH000352: Unable to release batch statement.. error in session.close() after rollback: Code:Con

通过maven test 报org.apache.ibatis.binding.BindingException: Invalid bound statement

背景 直接使用eclipse工具去执行,没有问题,通过testng.xml去执行,没有问题,但通过mvn clean test执行,就报错,提示org.apache.ibatis.binding.BindingException: Invalid bound statement 解决方法 首先先肯定的是:mybatis的配置是没有问题,因为eclipse可以正常执行: 在eclipse中把mapper的xml文件放到src代码目录下是可以一起打包进classes的,而maven去编译的时候不会,

JDBC接口介绍之Statement

声明:本栏目所使用的素材都是凯哥学堂VIP学员所写,学员有权匿名,对文章有最终解释权:凯哥学堂旨在促进VIP学员互相学习的基础上公开笔记. Statement: 1.介绍:对数据库数据一系列的操作依赖于SQL语句,而Statement接口是用来执行SQL语句的,Statement对象需要通过Connection类里面的createStatement()方法进行创建.如下图: 先创建一个连接数据库的类,以后就不用再写这片代码了,直接调用此类里面的方法即可: 创建Statement对象: 2.执行S

MyBatis3.4.0以上的分页插件错误:Could not find method on interface org.apache.ibatis.executor.statement.StatementHandler named prepare. Cause: java.lang.NoSuchMethodException: org.apache.ibatis.executor.stateme

错误: Could not find method on interface org.apache.ibatis.executor.statement.StatementHandler named prepare. Cause: java.lang.NoSuchMethodException: org.apache.ibatis.executor.statement.StatementHandler.prepare(java.sql.Connection)] with root cause 问题