Gear.Turbo

为什么建议使用多重using

什么是多重using(mutiple using)?

所谓多重using,是指C#的一个特性,即如下的代码结构:
using (IDisposable d1= ...)
using (IDisposable d2 = ...)
{
    
//operation with d1 and d2
}

而下面列的代码结构叫嵌套using(nested using):

using (IDisposable d1 = ...)
{
    
//operation with d1
    using(IDisposable d2 = ...)
    {
        
//operation with d1 and d2
    }
}

为什么建议使用多重using?

我们知道,使用using结构是用来处理IDisposable的对象。加入我们不用using结构,IDisposable的对象我们可以这样来处理:
IDisposable d1 = ...
//operation with d1
d1.Dispose();

这样做的缺点也很明显:1)开发者很容易忘记调用d1.Dispose()方法;2)也是最重要的,假如在d1.Dispose()之前发生了exception,那d1.Dispose()方法便不会被执行,于是d1便不能被释放。因此,对于这种模式的改进便是用try-finally的结构:

IDisposable d1 = ...
try
{
    
//operation with d1
}
finally
{
    d1.Dispose();
}

然后,这正是using的实现方式,因此我们使用using结构可使程序更优雅:

using(IDisposable d1 = ....)
{
    
//operation with d1
}

那么,当我们要同时处理多个IDisposable的对象时什么方式最好呢?我们首先会想到的是嵌套的using:

using (IDisposable d1= ...)
{
    
//Block 1
    
//operation with d1
    
using (IDisposable d2 = ...)
    {
        
//operation with d1 and d2
    }
}

它本身的缺点是,在Block1范围内无法访问到d2这个对象(当然如果要求在这个区域不能访问d2时另当别论)。另一个方面是,当有多重嵌套时,代码缩进很大,显的很不美观。解决很深的嵌套我们可以用try-finally的结构来实现:

IDisposable d1 = ... //instantiate d1
IDisposable d2 = ... //instantiate d2
IDisposable d3 = ... //instantiate d3
try
{
    
//operation with d1, d2, d3
}
finally
{
    d3.Dispose();
    d2.Dispose();
    d1.Dispose();
}

乍看起来没什么问题。但是,对于上面的代码假如d1,d2成功的实例化了,程序在执行到IDisposable d3 = ... //instantiate d3这条语句的时候发生了exception,下面的语句便不会执行。这样d1,d2便不能被正确的Dispose。作为解决办法,可以把对象的实例化放到try块中:

IDisposable d1 = null;
IDisposable d2 
= null;
IDisposable d3 
= null;
try
{
    IDisposable d1 
= ... //instantiate d1
    IDisposable d2 = ... //instantiate d2
    IDisposable d3 = ... //instantiate d3
    
//operation with d1, d2, d3
}
finally
{
    d3.Dispose();
    d2.Dispose();
    d1.Dispose();
}

这样,程序虽然丑陋了一些,但是似乎解决了对象实例化的时候发生异常的问题。等等,似乎新的问题又出现了:假如d1实例化的时候发生了exception,程序转到了finally语句块,这时d1,d2,d3中部分对象没有实例化,在没有实例化的对象上调用其方法就产生了null reference的exception。作为补救,程序变成了这个样子:

IDisposable d1 = null;
IDisposable d2 
= null;
IDisposable d3 
= null;
try
{
    IDisposable d1 
= ... //instantiate d1
    IDisposable d2 = ... //instantiate d2
    IDisposable d3 = ... //instantiate d3
    
//operation with d1, d2, d3
}
finally
{
    
if (d3 !=null) d3.Dispose();
    
if (d2 !=null) d2.Dispose();
    
if (d1 !=null) d1.Dispose();
}

最终,一个简单的需求让程序变的这么丑陋,我们要的是优雅的代码。对于这种情况,不被大家所了解的多重using可很好的解决问题:

using (IDisposable d1 = ...)
using (IDisposable d2 = ...)
using (IDisposable d3 = ...)
{
    
//do operation with d1, d2, d3
}

很简单哈?就是这么简单就可以解决前述的所有问题。值得一提,在这个结构中对象的释放顺序是和其生成顺序的倒序。

最后来一个实际应用的例子:
using (var file = new FileStream("c:\\a.txt", FileMode.Open))
using (var reader = new StreamReader(file))
{
    
//do with reader
}

 

因此能使用多重using就要使用。

posted on 2010-01-10 17:42  lsp  阅读(406)  评论(1编辑  收藏  举报

导航