[转]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 ...
随机推荐
- crontab演出newLISP脚本设置环境变量
今天遇到一个问题.执行在终端newLISP文字,一切正常,搬去crontab在将无法正常工作.即使crontab -e命令是在同一个用户执行.还是有问题. 因为newLISP脚本使用hive和hado ...
- JNDI数据源配置注意事项
假设是在原有project上改动 1,applicationContext.xml中改动当中的value值 <bean id="dataSource" class=" ...
- 【SSH三框架】Hibernate基金会七:许多附属业务
相对于上述一关系,在这里,下一个一对多关系说明. 另外,在上述.我们描述了许多人描述的一一对应关系.在关系数据库是多对一的关系.但也有许多关系. 但,只知道它是不够的,Hibernate它是一种面向对 ...
- Ubuntu 15.10 x64 安装 Android SDK(转)
操作系统:Ubuntu 15.10 x64 目标:安装 Android SDK 本文最后更新时间:2015-11-3 安装32位库文件 2013年9月的iPhone 5s是第一款64位手机,而Andr ...
- js之按键总结
js 实现键盘记录 兼容FireFox和IE 2009-01-07 11:43 作者:羽殇仁 转载请注明出处,谢谢. 本篇文章是我的第一百篇blog文章,恭喜一下! 这两天突然想弄弄js的键盘记录,所 ...
- Android ADT Bundle(Eclipse with ADT) ADT R23
ADT Bundle Platform Package Size MD5 Checksum Windows 32-bit adt-bundle-windows-x86-20140624.zip 377 ...
- java Json字符串转List<Map>类型
//相关包 import com.google.gson.Gson; import com.google.gson.JsonElement; import com.google.gson.JsonPa ...
- c++的vector容器
c++还有一个很常用的容器就是vector容器,他是数组实现的,是一种可变长的容器,在很多的时候可以简化我们的编程.可学习的链接:http://www.cnblogs.com/mr-wid/archi ...
- android登录界面
在网上在到一个登录界面感觉挺不错的,给大家分享一下~先看效果图: 这个Demo除了按钮.小猫和Logo是图片素材之外,其余的UI都是通过代码实现的. 一.背景 背景蓝色渐变,是通过一个xml文件来设置 ...
- paramiko socket.error: Int or String expected
paramiko socket.error: Int or String expected paramiko的环境: Python 2.6.6 paramiko==1.14.0 正常的paramiko ...