C# 多次处理对象

C# 多次处理对象,c#,ca2202,C#,Ca2202,我有以下代码,它使用流打开和修改打开的XML文档,然后保存该流的新二进制表示形式: MemoryStream stream = null; try { stream = new MemoryStream(); stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length); using (WordprocessingDocument document = Wor

我有以下代码,它使用流打开和修改打开的XML文档,然后保存该流的新二进制表示形式:

MemoryStream stream = null;
try
{
    stream = new MemoryStream();
    stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);

    using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
    {
        OfficeDocument.ModifyDocument(document);
        this.SetBinaryRepresentation(stream.ToArray());
        stream = null;
    }
}
finally
{
    if (stream != null)
    {
        stream.Dispose();
    }
}
我最初使用了两个using块(一个用于MemoryStream,第二个用于WordprocessingDocument),但收到了警告CA2202:“对象‘流’可以在方法中多次释放……”根据,我将代码修改为上面的(将外部using转换为try),但我仍然收到了此警告


我不确定如何构造此方法以确保在流上只调用一次Dispose。我不想简单地压制这一警告,因为MSDN文章指出,您不应该依赖Dispose多次安全调用。

using语句处理对象-因此本质上,您两次调用Dispose多次处理对象应该总是安全的。从:

如果对象的Dispose方法被多次调用,则该对象必须忽略第一次调用之后的所有调用。如果多次调用该对象的Dispose方法,则该对象不得引发异常


话虽如此,使用语句无疑是解决问题的方法。您收到该方法的唯一原因是,如果您显式地处理了该对象(这不是必需的),因为using语句应该始终只处理该对象一次。

如果在using块中抛出异常,则在将流设置为null之前,流仍可能被处理两次。试试这个:

MemoryStream stream = null;
MemoryStream streamToDispose = null;
try
{
    streamToDispose = stream = new MemoryStream();
    stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);

    using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
    {
        streamToDispose = null;
        OfficeDocument.ModifyDocument(document);
        this.SetBinaryRepresentation(stream.ToArray());
    }
}
finally
{
    if (streamToDispose != null)
    {
        streamToDispose.Dispose();
    }
}

当代码离开
WordProcessingDocument
周围的using块时,它将调用dispose

using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))

由于
WordProcessingDocument
在其构造函数中获取了
stream
的一个实例,因此当调用
WordProcessingDocument.dispose
时,它将在该流实例上调用dispose。然后进入finally块,在该块中调用
stream.Dispose()
——您现在已经对stream实例调用了两次Dispose()。

MSDN文章中的示例不适用于您的原因是他们一进入using块就将流设置为null,而您在using块中使用流,并在之后将流设置为null。如果在
stream=null
语句之前抛出异常,
stream
将在using块退出时处理,然后再次在finally块中处理

不幸的是,由于您需要在
document
更新流之后访问流,因此我看不到一种干净的方法可以在using语句中使用他们设置
stream=null
的示例来避免多个
Dispose()
调用。另一种方法是,您可以在try块外声明
文档
,然后在finally块内清理这两个文件,如下所示:

MemoryStream stream = null;
WordprocessingDocument document = null;
try
{
    stream = new MemoryStream();
    stream.Write(this.GetBinaryRepresentation(), 0, this.GetBinaryRepresentation().Length);

    document = WordprocessingDocument.Open(stream, true));

    OfficeDocument.ModifyDocument(document);
    this.SetBinaryRepresentation(stream.ToArray()); 
}
finally
{
    if( document != null)
    {
        document.Dispose();
    }
    // Catch the case where an error occurred before document was defined.
    else
    {
        stream.Dispose();
    }
}

只是一个注释:当您在使用块中引用引用时,在流中如何调用<代码>处理< /代码>?@ BRANANRASMISSENN -考虑流。在这种情况下,流没有被设置为null,并在finally块中被释放。@Henrik:当然,但在成功的情况下,我看不到如何调用
Dispose
。流是通过文档周围的using语句被释放的。当它超出范围时,会调用document.Dispose(),它本身会处理任何底层资源,包括传入的流。@JonSenchyna啊,我不知道WordprocessingDocument类,所以我无法判断它是否也处理了流。感谢您的澄清。谢谢,但是根据代码分析规则的文档,您不应该依赖于:“不要抑制来自此规则的警告。即使已知Dispose for the object可多次安全调用,该实现在将来可能会更改。”@Andrew,我认为这很可笑,正如合同上说的,它应该永远是安全的。也就是说,没有理由手动处理它——只需使用using语句,其他什么都不用。他们没有办法强制执行契约,因为IDisposable是一个接口,他们无法控制它的每个实现。我确实同意,对于框架代码来说,它至少应该始终是正确的。@JonSenchyna问题是——任何不遵守合同的东西都是一个bug——简单明了。编写所有代码以避免第三方错误,而不是修复错误,这在我看来是过火了。我宁愿相信合同,报告违反合同的情况,也不担心避免理论问题。是的,我在想也许WordProcessingDocument为您包装了dispose,但也许不是。。。!您也可以事先简单地获取stream.ToArray(),然后在输入using块时将原始流设置为null。将引用设置为null不会更改yoru WordprocessingDocument使用的基础对象的状态。我认为这不可行,OfficeDocument.ModifyDocument方法正在更改流,并且根据MemoryStream.ToArray()文档()ToArray在该时间点返回数组的副本,因此,流后缀的更改不会反映在二进制表示中。不管怎样,我确实试过了,但是单元测试失败了。bc二进制表示仍然包含原始文档。啊,你是对的。我没有仔细阅读您的using语句的内容。我更新了我的答案,以使用我最初建议的单个try/finally块,而不是带有嵌套using块的try/finally。这样就可以解决您的问题,而不必巧妙地操纵引用。