什么是多重using(mutiple using)?
using (IDisposable d2 = ...)
{
//operation with d1 and d2
}
而下面列的代码结构叫嵌套using(nested using):
{
//operation with d1
using(IDisposable d2 = ...)
{
//operation with d1 and d2
}
}
为什么建议使用多重using?
//operation with d1
d1.Dispose();
这样做的缺点也很明显:1)开发者很容易忘记调用d1.Dispose()方法;2)也是最重要的,假如在d1.Dispose()之前发生了exception,那d1.Dispose()方法便不会被执行,于是d1便不能被释放。因此,对于这种模式的改进便是用try-finally的结构:
try
{
//operation with d1
}
finally
{
d1.Dispose();
}
然后,这正是using的实现方式,因此我们使用using结构可使程序更优雅:
{
//operation with d1
}
那么,当我们要同时处理多个IDisposable的对象时什么方式最好呢?我们首先会想到的是嵌套的using:
{
//Block 1
//operation with d1
using (IDisposable d2 = ...)
{
//operation with d1 and d2
}
}
它本身的缺点是,在Block1范围内无法访问到d2这个对象(当然如果要求在这个区域不能访问d2时另当别论)。另一个方面是,当有多重嵌套时,代码缩进很大,显的很不美观。解决很深的嵌套我们可以用try-finally的结构来实现:
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 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 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 d2 = ...)
using (IDisposable d3 = ...)
{
//do operation with d1, d2, d3
}
很简单哈?就是这么简单就可以解决前述的所有问题。值得一提,在这个结构中对象的释放顺序是和其生成顺序的倒序。
using (var reader = new StreamReader(file))
{
//do with reader
}