C#CA2000:使用FileStream / XmlTextReader在丢失范围之前处置对象

时间:2010-06-27 18:49:21

标签: c# dispose using ca2000 ca2202

我有很多像这样的代码:

FileStream fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open); 
using (XmlTextReader reader = new XmlTextReader(fs)) 
{ 
   /* Some other code */
}

这给了我以下代码分析警告:

CA2000 : Microsoft.Reliability : In method 'SF_Tester.Run()', object 'fs' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'fs' before all references to it are out of scope.

如果我按照建议操作并将File.Open放入using语句中,我会得到:

CA2202 : Microsoft.Usage : Object 'fs' can be disposed more than once in method 'SF_Tester.Run()'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 39

我正在使用VS2010而且我不禁想到我做错了什么但是我没有看到它。 我做错了什么?

7 个答案:

答案 0 :(得分:15)

叹息,不是很累。通过使用推荐的Create()方法避免所有这些:

 using (var reader = XmlReader.Create(@"C:\Temp\SNB-RSS.xml")) {
     //...
 }

答案 1 :(得分:11)

由于没有人提供解决此问题的解决方案,我正在编写我的工作解决方案:

FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite,    FileShare.ReadWrite);
try
{
   using (var fileWriter = new StreamWriter(fs, encoding))
   {
       fs = null;
       fileWriter.Write(content);
    }
 }
 finally
 {
     if (fs != null)
         fs.Dispose();
 }

这将删除CA2000。

答案 2 :(得分:1)

我只是猜测;现在没有时间进行全面分析。

假设XmlTextReader构造函数'获取传入的流的所有权',因此处理XmlTextReader也将Dispose基础流。这可以解释你看到的行为。也许XmlTextReader构造函数可以抛出,在这种情况下,关于fs的原始警告是有意义的。但是,考虑到这个假设,这段代码

        var fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open);
        XmlTextReader reader = null;
        try
        {
            reader = new XmlTextReader(fs);
        }
        finally
        {
            if (reader== null)
            {
                fs.Dispose();
            }
        }
        if (reader != null)
        {
            using (reader)
            {
                /* Some other code */
            }
        }
我认为,

是正确的,但仍会产生虚假警告。这闻起来就是一个很好的例子,它展示了静态分析工具的局限性。

正如其他人所说,还有另一个API可以直接从文件名(XmlReader.Create())创建阅读器,这可以避免所有这些(并显示精心设计的以场景为中心的API是一件令人惊讶的好事各种原因)。

答案 3 :(得分:1)

这是一个已知问题

http://connect.microsoft.com/VisualStudio/feedback/details/535118/ca2000-and-ca2202-offer-contradictory-warnings

如果您使用的是StreamWriter而不是XmlTextReader(如上面的解决方案中所示),您可以通过相关的构造函数使用类似的方法; e.g。

var sw = new StreamWriter("filename.txt");

var sw = new StreamWriter("filename.txt", /*append to file = */ false );

从文档中不清楚第一种形式的构造函数是否会覆盖或附加到文件。

答案 4 :(得分:0)

正如this回答中所提到的,正确解决它的唯一方法是执行as recommended in CA2202并使用外部try-finally块而不是外部使用块。在内部使用中,将外部IDisposable对象设置为null,以防止在内部使用完成后访问它。

这是一个“正确”执行它的通用包装器,即在设计糟糕的XmlReader周围工作(也许它不应该取得它收到的流的所有权?不确定正确的方法是什么)

免责声明:未经过真正测试

public static TResult SafeNestedUsing<TOuter, TInner, TResult>(Func<TOuter> createOuterDisposable, Func<TOuter, TInner> createInnerDisposable, Func<TInner, TResult> body)
        where TInner : IDisposable
        where TOuter : class, IDisposable
    {
        TOuter outer = null;
        try
        {
            outer = createOuterDisposable();
            using (var inner = createInnerDisposable(outer))
            {
                var result = body(inner);
                outer = null;
                return result;
            }
        }
        finally
        {
            if (null != outer)
            {
                outer.Dispose();
            }
        }
    }

使用示例:

SafeNestedUsing<MemoryStream, XmlReader, XmlDocument>(
    ()          => new MemoryStream(array),
    (memStream) => XmlReader.Create(memStream, xmlReaderSettings),
    (xmlReader) =>
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);
        return xmlDoc;
    });

这个非常笨重,你可能会认为最好重复try / set null / finally模式。但是对于嵌套使用的重复模式,我宁愿这样做,也不是每次重复完整的事情。

答案 5 :(得分:0)

只需使用&#39;使用&#39;对于文件流

 using(FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// some codes here

}

不要修改fs,也不要在花括号里面使用fs.close()。

答案 6 :(得分:-1)

在FileStream本身上也使用using语句,就像在XmlTextReader上一样。

http://msdn.microsoft.com/en-us/library/system.io.filestream(VS.71).aspx

Grz,Kris。