[转]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 ...
随机推荐
- PS多形式的部分之间复制“笨办法”
PS剪切页面,有时候你可能会遇到这样的情况:设计改进,但是,我们要具有相同的切片. 在此假设,可以直接用于切割片.我们可以节省大量的时间,又分为片. 但是,人们一般不会在你的上跨片设计PSD在变化,但 ...
- javascript系列之DOM(三)---事件
原文:javascript系列之DOM(三)---事件 事件是javascript跳动的心脏,是DOM所有成分结合的万金油.当我们在WEB 上进行某些交互时,事件也就发生了.点击某些内容,鼠标经过特定 ...
- Android - 警告:it is always overridden by the value specified in the Gradle build script
警告:it is always overridden by the value specified in the Gradle build script 本文地址: http://blog.csdn. ...
- Office转HTML
/// <summary> /// word转成html /// </summary> /// <param name="path"></ ...
- crawler_网络爬虫中编码的正确处理与乱码的解决策略
转载: http://hi.baidu.com/erliang20088/item/9156132bdaeae8949c63d134 最近一个月一直在对nutch1.6版进行中等层次的二次开发,本来是 ...
- crawler_微信采集方案
仅供参考
- 【github课程】创建github仓库和库创建一个版本号,并添加到存储库文件的版本号
首先,需要登录github注册一个帐号:https://github.com/ 注册帐号登录后,.在右上角,"新仓库": 然后会出现例如以下的界面: 仅仅须要输入仓库的名称,描写叙 ...
- mac os x10.11.2系统eclipse无法读取环境变量的问题
eclipse调试Android自动化脚本的时候一直无法找到adb,遇到这么坑的问题,折腾死了,记录一下. mac os x10.11.2系统GUI程序(eclipse)无法读取~/.bash_pro ...
- Hack 语言学习/参考---1.1 What is Hack?
What is Hack?¶ Hack is a language for HHVM that interopates seamlessly with PHP. The barrier to entr ...
- [Unity3D]Unity3D连衣裙实现游戏开发系统
大家好,我是秦培.欢迎关注我的博客,我的博客地址">blog.csdn.net/qinyuanpei. 不知从什么时候開始,国产RPG单机游戏開始出现换装,仙剑系列中第一部实现了换装的 ...