Утилизация объекта несколько раз
У меня есть следующий код, который использует поток, чтобы открыть и изменить документ Open 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();
}
}
Первоначально я использовал два блока с использованием (один для MemoryStream и второй для WordprocessingDocument), но получил предупреждение CA2202: "Объект" поток "может быть удален более одного раза в методе..." В статье MSDN я изменил код выше (преобразование внешнего использования в попытку), но я все еще получаю это предупреждение.
Я не уверен, как я могу структурировать этот метод, чтобы гарантировать, что Dispose вызывается ровно один раз в потоке. Я бы предпочел не просто подавлять это предупреждение, поскольку в статье MSDN говорится, что вы не должны полагаться на то, что Dispose безопасно вызывается несколько раз.
5 ответов
Причина, по которой пример из статьи MSDN не сработал для вас, заключается в том, что они устанавливают для потока значение null, как только они входят в блок using, тогда как вы используете поток внутри вашего блока using и после этого устанавливаете для потока значение null. Если исключение выдается перед вашим stream = null
заявление, stream
будет удален при выходе из блока using, а затем снова в ваш блок finally.
К сожалению, так как вам нужно получить доступ к вашему потоку после document
обновил его, я не вижу чистого способа использовать их пример настройки stream = null
в вашем заявлении об использовании, чтобы избежать множественного Dispose()
звонки. Альтернативой было бы, чтобы вы могли объявить оба stream
а также document
за пределами блока try, а затем, наконец, вычистите их обоих внутри, вот так:
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();
}
}
Многократная утилизация объекта всегда должна быть безопасной. Из документации по утилизации:
Если метод Dispose объекта вызывается более одного раза, объект должен игнорировать все вызовы после первого. Объект не должен вызывать исключение, если его метод Dispose вызывается несколько раз.
При этом заявление об использовании определенно является подходящим способом. Единственная причина, по которой вы получили бы этот метод, заключалась в том, что вы явно избавлялись от объекта, что не требовалось, так как оператор using всегда должен располагать объект ровно один раз.
Поток может все еще быть удален дважды, если исключение выдается в блоке использования, прежде чем поток будет установлен в нуль. Попробуй это:
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();
}
}
Оператор using удаляет объект - поэтому, по сути, вы вызываете dispose дважды
Когда ваш код покидает блок использования вокруг WordProcessingDocument
Позвонит распоряжаться.
using (WordprocessingDocument document = WordprocessingDocument.Open(stream, true))
Так как WordProcessingDocument
берет пример stream
в своем конструкторе он вызовет dispose для этого экземпляра потока, когда WordProcessingDocument.Dispose
называется. Затем вы вводите блок finally, куда вы звоните stream.Dispose()
- теперь вы дважды вызывали Dispose() для экземпляра потока.