[转]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 ...
随机推荐
- 光流和KLT
一 光流 光流的概念是Gibson在1950年首先提出来的. 它是空间运动物体在观察成像平面上的像素运动的瞬时速度.是利用图像序列中像素在时间域上的变化以及相邻帧之间的相关性来找到上一帧跟当前帧之间存 ...
- NYNU_省赛选拔题(5)
题目描述 P 的一家要出去旅游,买了当地的地图,发现各地分别由各个景点,若 P 想使家人分队去景点,尽快到达各个景点(必须所有景点),并且最终所有家人都到达 M 所在的景点. 你用程序告诉 P 最 ...
- ASP.NET Web API中使用OData
在ASP.NET Web API中使用OData 一.什么是ODataOData是一个开放的数据协议(Open Data Protocol)在ASP.NET Web API中,对于CRUD(creat ...
- Office——检索 COM 类工厂中 CLSID 为 {000209FF-0000-0000-C000-000000000046} 的组件时失败
检索 COM 类工厂中 CLSID 为 {000209FF-0000-0000-C000-000000000046} 的组件时失败,原因是出现以下错误: 8000401a 1.运行dcomcnfg.e ...
- ASP.NET 5简介
ASP.NET 5简介 解读ASP.NET 5 & MVC6系列(1):ASP.NET 5简介 2015-05-13 09:14 by 汤姆大叔, 3379 阅读, 39 评论, 收藏, 编辑 ...
- jQuery 的 serializeArray()、serialize() 方法
serializeArray()方法说明: 实例 输出以数组形式序列化表单值的结果: $("button").click(function(){ x=$("form&qu ...
- iterm2 快捷键大全 Mac item2常用快捷键
整理使用 iTerm 2 过程中得常用快捷键,Mac 原来自带的终端工具 Terminal 不好用是出了名的,虽然最近几个版本苹果稍微做了些优化,功能上,可用性方面增强不少,无奈有个更好用的 Iter ...
- Android Volley彻底解决(三),定制自己Request
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/17612763 经过前面两篇文章的学习,我们已经掌握了Volley各种Request ...
- Visual Studio-Sequence Diagram
UML Design Via Visual Studio-Sequence Diagram 本文主要介绍在Visual Studio中设计时序图,内容如下: 何时使用时序图 时序图元素介绍 条件.循环 ...
- 使用myeclipse将Javaj项目标ar套餐邂逅classnotfound解决问题的方法
做一件事的今天,该Java项目打包成jar文件.折腾2小时,最终运行jar文件报告classnotfound异常,我觉得程序写入依赖jar包不玩成,但是,我手动添加.或不.网上找了很多办法.或不.后. ...