[转]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的更多相关文章
- Oracle 课程八之性能优化之10053事件
一. 10053事件 当一个SQL出现性能问题的时候,可以使用SQL_TRACE 或者 10046事件来跟踪SQL. 通过生成的trace来了解SQL的执行过程. 我们在查看一条SQL的执行计划的时候 ...
- 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 ...
- 针对数据泵导出 (expdp) 和导入 (impdp)工具性能降低问题的检查表 (文档 ID 1549185.1)
针对数据泵导出 (expdp) 和导入 (impdp)工具性能降低问题的检查表 (文档 ID 1549185.1) 文档内容 适用于: Oracle Database – Enterprise Edi ...
- org.apache.ibatis.binding.BindingException: Invalid bound statement (not found):
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): da.huying.usermanag ...
- [解决方案]CREATE DATABASE statement not allowed within multi-statement transaction.
CREATE DATABASE statement not allowed within multi-statement transaction. 刚开始报这个错误的时候,我上度娘搜了一下. 别人是在 ...
- 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 ...
- JDBC中的Statement和PreparedStatement的区别
JDBC中的Statement和PreparedStatement的区别
- SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问
delphi ado 跨数据库访问 语句如下 ' and db = '帐套1' 报错内容是:SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATE ...
- 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 ...
随机推荐
- sqlplus登录问题
问题1.sqlplus login -- SP2-0750: You may need to set ORACLE_HOME to your Oracle software directory 在/e ...
- tortoise svn无法识别subversion check向下代码来解决
使用eclipse小工具subversion check代码后,tortoise svnclient(版本号1.8.8)无法识别,不显示svn图标. 根据每个试验后的线上不实际的解决方案.试过的方法: ...
- Linux互斥和同步应用程序(一):posix线程和线程之间的相互排斥
[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet 或 .../gentleliu,文章仅供学习交流.请勿用于商业用途] 有了进程的概念,为何还要使用线程呢? 首先,回 ...
- gcd&&lcm
1011 最大公约数GCD 基准时间限制:1 秒 空间限制:131072 KB 分值: 0 难度:基础题 收藏 关注 输入2个正整数A,B,求A与B的最大公约数. Input 2个数A,B,中 ...
- HTML文档中应用css样式的方法总结
在HTML文档中应用css样式大致有三种方法:1.link标签链接外部样式表:2.使用style元素包含样式表:3.使用style属性,即内联样式 一.link标签链接外部样式表 先看一条较为标准的l ...
- DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能
DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能 一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己 ...
- unity3d NGUI入门(描述和使用插件参数)
我用NGUI它是3.5.4,Unity3d版本号是4.3.4f需要 NGUI3.5.4 下载NGUI,这是破解版的,用于学习.假设是商用.请支持正版 插件的导入 1.NGUI的导入,双击NGUI Ne ...
- JAVA8,SPRING,ANGULARJS对项目
java8+spring+angularjs 项目应用 最近有写一个电子订单商务网站,使用JAVA8,SPRING,ANGULARJS对项目使用的技术和大家分享. 第一次写博客,哪有不对需要改正的请联 ...
- python_基础学习_02_拆分文件(spilt)
做爬虫经常会有这样的引用场景 ,原始网页存储格式为 url+\t+ html php 有个explode的 拆分文本行方法,比较方便直接接收列值 list($url,$html)=explode(& ...
- STL该反应堆运行
首先来看全然二叉树的定义: 若设二叉树的深度为h,除第 h 层外,其他各层 (1-h-1) 的结点数都达到最大个数,第 h 层全部的结点都连续集中在最左边,这就是全然二叉树.而将一维数组视为全然二叉树 ...