防止多次处理对象的释放问题

8

请看下面的代码:

using (Stream stream = new FileStream("file.txt", FileMode.OpenOrCreate))
{
    using (StreamWriter writer = new StreamWriter(stream))
    {
        // Use the writer object...
    }
}

writer流被处理时,它会内部处理FileStream stream的释放。

除了MSDN建议在finally语句中处理外部使用的流之外,还有其他的设计吗?

Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.OpenOrCreate);
    using (StreamWriter writer = new StreamWriter(stream))
    {
        stream = null;
        // Use the writer object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

5
根据微软的说法,您应该能够多次调用相同对象的Dispose()方法。 - Matthew Watson
1
@MatthewWatson,能够引用微软的相关参考资料会更好。 - Mike Dinescu
@MatthewWatson - 不幸的是,文档与警告不一致:“不要抑制此规则的警告。即使已知对于该对象的Dispose方法可以多次安全调用,但实现在未来可能会更改。”见http://msdn.microsoft.com/en-us/library/vstudio/ms182334%28v=vs.110%29.aspx。 - Mike Dinescu
我并不是说你在多次调用Stream的dispose方法时是错误的,只是根据微软的文档来看,这个警告有些奇怪。 - Mike Dinescu
@MatthewWatson,你说得对,我假设这里可能会有内存泄漏问题。我测试了你发布的代码,它可以工作。但是如果没有正确实现dispose方法,则多次处理可能会出现问题。感谢您澄清这一点。 - CloudyMarble
显示剩余7条评论
2个回答

3
这是一个FxCop与.NET Framework设计选择强烈不同的案例。问题是由于StreamWriter假设拥有流而引起的。这通常是一种“成功之坑”的设计选择,大多数程序员会认为关闭StreamWriter就足以使流被处理。特别是当他们使用Close()而不是Dispose()时。
这在绝大多数情况下都很好用。但在CryptoStream中有一种特定用法非常棘手。当底层流在CryptoStream刷新和处理之前关闭时,它将无法诊断地发生故障。这种情况下,FxCop警告可能是适当的,尽管它太晦涩难懂,很难识别具体的问题 ;)
还有一般情况,即程序员编写了自己的Dispose()方法,但忘记使其安全,以免被调用超过一次。这正是FxCop警告旨在引起注意的问题,否则它并不聪明到能够看到Dispose方法实际上是安全的。
在这种特定情况下,FxCop警告是毫无用处的。所有.NET Framework提供的Dispose()方法实现都是安全的。FxCop应该自动抑制此类警告以针对.NET框架代码。但它没有,Microsoft也在使用它。在.NET框架源代码中有很多[SuppressMessage]属性。
解决警告的方法太丑陋和容易出错。而且没有意义,因为实际上什么都不会出错。请记住,FxCop只是一种诊断工具,旨在生成“您考虑过这个吗”的消息。它不是一个警察,当您忽略规则时,它不会把您送进监狱。那是编译器的工作。
使用[SuppressMessage]属性关闭警告。

微软使用SupressesMessage来抑制警告,这真的很有趣。很多人将这些规则视为编程圣经。在我的情况下,我使用了StringWriter和TextXmlWriter,所以我必须将字符串保存在临时变量中,然后才能像MSDN建议的那样将StringWriter设置为null。 - CloudyMarble

3
这种情况的解决方案是调用StreamWriter构造函数的重载版本,让它不要处理基础流

不幸的是,这只适用于 .Net 4.5;否则,你必须继续做你已经在做的事情。

此外,请查看此线程:有没有办法关闭 StreamWriter 而不关闭其 BaseStream?

顺便说一下,当我尝试运行 OP 中的代码时,并不会引发异常!

下面的示例假定存在名为 "C:\TEST" 的文件夹:

using System;
using System.IO;

namespace Demo
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            // This does NOT cause any exceptions:

            using (Stream stream = new FileStream("c:\\test\\file.txt", FileMode.OpenOrCreate))
            {
                using (StreamWriter writer = new StreamWriter(stream))
                {
                    writer.Write("TEST");
                }
            }
        }
    }
}

你为什么在谈论异常?这是代码分析警告。 - Hans Passant
只是一条信息:在我的情况下,我注意到即使是 .net 4.5 也无法帮助我,因为在我的情况下,“内部”流是 XmlTextWriter,它没有 leaveOpen 标志! - CloudyMarble

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接