题
在 C# 中,如果我想确定性地清理非托管资源,我可以使用“using”关键字。但对于多个依赖对象,这最终会嵌套得越来越深:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
{
using (BufferedStream bs = new BufferedStream(fs))
{
using (StreamReader sr = new StreamReader(bs))
{
// use sr, and have everything cleaned up when done.
}
}
}
在 C++ 中,我习惯于使用析构函数来执行以下操作:
{
FileStream fs("c:\file.txt", FileMode.Open);
BufferedStream bs(fs);
StreamReader sr(bs);
// use sr, and have everything cleaned up when done.
}
C# 有更好的方法来做到这一点吗?或者我是否陷入了多层嵌套?
解决方案
您不必嵌套多次使用:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
// all three get disposed when you're done
}
其他提示
您可以将 using 语句放在左大括号之前,如下所示:
using (StreamWriter w1 = File.CreateText("W1"))
using (StreamWriter w2 = File.CreateText("W2"))
{
// code here
}
您可以使用以下语法来压缩一些内容:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
}
这是罕见的情况之一,恕我直言,不对所有块使用 { } 是有意义的。
您可以手动写出 .Dispose 调用,而不是嵌套 using 语句 - 但您几乎肯定会在某些时候错过一个。
运行 FxCop 或其他可以确保所有 IDisposable 实现类型实例都有 .Dispose() 调用的程序,或者处理嵌套。
我已经实施了类似的解决方案 迈克尔·梅多斯是之前的,但是他的 StreamWrapper
代码不考虑如果 Dispose()
对成员变量调用的方法由于某种原因抛出异常,随后的 Dispose()
es 不会被调用,资源可能会悬空。更安全的工作方式是:
var exceptions = new List<Exception>();
try
{
this.sr.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
try
{
this.bs.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
try
{
this.fs.Dispose();
}
catch (Exception ex)
{
exceptions.Add(ex);
}
if (exceptions.Count > 0)
{
throw new AggregateException(exceptions);
}
}
您可以省略花括号,例如:
using (FileStream fs = new FileStream("c:\file.txt", FileMode.Open))
using (BufferedStream bs = new BufferedStream(fs))
using (StreamReader sr = new StreamReader(bs))
{
// use sr, and have everything cleaned up when done.
}
或使用常规的 try finally 方法:
FileStream fs = new FileStream("c:\file.txt", FileMode.Open);
BufferedStream bs = new BufferedStream(fs);
StreamReader sr = new StreamReader(bs);
try
{
// use sr, and have everything cleaned up when done.
}finally{
sr.Close(); // should be enough since you hand control to the reader
}
这使得代码行数变得更大,但可读性也得到了切实的提升:
using (StreamWrapper wrapper = new StreamWrapper("c:\file.txt", FileMode.Open))
{
// do stuff using wrapper.Reader
}
其中 StreamWrapper 定义如下:
private class StreamWrapper : IDisposable
{
private readonly FileStream fs;
private readonly BufferedStream bs;
private readonly StreamReader sr;
public StreamWrapper(string fileName, FileMode mode)
{
fs = new FileStream(fileName, mode);
bs = new BufferedStream(fs);
sr = new StreamReader(bs);
}
public StreamReader Reader
{
get { return sr; }
}
public void Dispose()
{
sr.Dispose();
bs.Dispose();
fs.Dispose();
}
}
经过一些努力,StreamWrapper 可以被重构为更加通用和可重用。
应该注意的是,通常当基于另一个流创建流时,新流将关闭传入的流。因此,为了进一步减少你的例子:
using (Stream Reader sr = new StreamReader( new BufferedStream( new FileStream("c:\file.txt", FileMode.Open))))
{
// all three get disposed when you're done
}
对于这个例子,我们假设您有:
c:\ 下名为 1.xml 的文件
名为 textBox1 的文本框,其多行属性设置为 ON。
const string fname = @"c:\1.xml";
StreamReader sr=new StreamReader(new BufferedStream(new FileStream(fname,FileMode.Open,FileAccess.Read,FileShare.Delete)));
textBox1.Text = sr.ReadToEnd();
using 语句是语法糖,可转换为:
try
{
obj declaration
...
}
finally
{
obj.Dispose();
}
您可以在对象上显式调用 Dispose,但它不会那么安全,因为如果其中一个抛出异常,则资源将无法正确释放。