Ошибка "Объект может быть удален более одного раза"

Когда я запускаю анализ кода на следующем фрагменте кода, я получаю это сообщение:

Объект "поток" может быть размещен более одного раза в методе "upload.Page_Load(object, EventArgs)". Чтобы избежать создания System.ObjectDisposedException, вы не должны вызывать Dispose более одного раза для объекта.

using(var stream = File.Open(newFilename, FileMode.CreateNew))
using(var reader = new BinaryReader(file.InputStream))
using(var writer = new BinaryWriter(stream))
{
    var chunk = new byte[ChunkSize];
    Int32 count;
    while((count = reader.Read(chunk, 0, ChunkSize)) > 0)
    {
        writer.Write(chunk, 0, count);
    }
}

Я не понимаю, почему он может быть вызван дважды, и как это исправить, чтобы устранить ошибку. Любая помощь?

6 ответов

Я боролся с этой проблемой и нашел пример здесь очень полезным. Я выложу код для быстрого просмотра:

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

Замените внешний оператор using на оператор try/finally, убедившись, что ОБА обнуляет поток после использования его в StreamWriter И, чтобы убедиться, что он не равен нулю в окончании перед удалением.

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();
}

Это очистило мои ошибки.

Для иллюстрации давайте отредактируем ваш код

using(var stream = File.Open(newFilename, FileMode.CreateNew))
{
    using(var reader = new BinaryReader(file.InputStream))
    {
        using(var writer = new BinaryWriter(stream))
        {
            var chunk = new byte[ChunkSize];
            Int32 count;
            while((count = reader.Read(chunk, 0, ChunkSize)) > 0)
            {
                writer.Write(chunk, 0, count);
            }
        } // here we dispose of writer, which disposes of stream
    } // here we dispose of reader
} // here we dispose a stream, which was already disposed of by writer

Чтобы избежать этого, просто создайте писателя напрямую

using(var reader = new BinaryReader(file.InputStream))
    {
        using(var writer = new BinaryWriter( File.Open(newFilename, FileMode.CreateNew)))
        {
            var chunk = new byte[ChunkSize];
            Int32 count;
            while((count = reader.Read(chunk, 0, ChunkSize)) > 0)
            {
                writer.Write(chunk, 0, count);
            }
        } // here we dispose of writer, which disposes of its inner stream
    } // here we dispose of reader

edit: чтобы принять во внимание то, что говорит Эрик Липперт, действительно может быть момент, когда поток освобождается финализатором, только если BinaryWriter выдает исключение. Согласно коду BinaryWriter, это может произойти в трех случаях

  If (output Is Nothing) Then
        Throw New ArgumentNullException("output")
    End If
    If (encoding Is Nothing) Then
        Throw New ArgumentNullException("encoding")
    End If
    If Not output.CanWrite Then
        Throw New ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable"))
    End If
  • если вы не указали вывод, т.е. если поток равен нулю. Это не должно быть проблемой, поскольку нулевой поток означает отсутствие ресурсов для удаления:)
  • если вы не указали кодировку. поскольку мы не используем форму конструктора, в которой указана кодировка, здесь также не должно возникнуть никаких проблем (я не слишком много смотрел на конструктор кодирования, но неправильная кодовая страница может выдать)
    • если вы не передаете записываемый поток. Это должно быть поймано довольно быстро во время разработки...

Во всяком случае, хороший момент, следовательно, редактировать:)

BinaryReader/BinaryWriter будет распоряжаться базовым потоком для вас при его удалении. Вам не нужно делать это явно.

Чтобы исправить это, вы можете удалить использование самого потока.

Правильная реализация Dispose явно требуется, чтобы не заботиться о том, что он вызывался более одного раза для одного и того же объекта. Хотя множественные вызовы Dispose иногда указывают на логические проблемы или код, который можно было бы лучше написать, единственный способ улучшить исходный опубликованный код - убедить Microsoft добавить опцию в BinaryReader и BinaryWriter, инструктируя их не удалять свои переданные в потоке (и затем используйте эту опцию). В противном случае код, необходимый для обеспечения того, чтобы файл закрывался, даже если читатель или писатель добавляет его конструктор, был бы достаточно уродливым, чтобы простое удаление файла более одного раза показалось бы более чистым.

Ваш писатель будет распоряжаться вашим потоком всегда.

Подавляйте CA2202 всякий раз, когда вы уверены, что рассматриваемый объект правильно обрабатывает множественные вызовы и что ваш поток управления безупречно читаем. Объекты BCL обычно реализуют правильно. Потоки славятся этим.

Но не обязательно доверять сторонним или своим собственным потокам, если у вас еще нет модульных тестов, исследующих этот сценарий. API, который возвращает может возвращать хрупкий подкласс.

Другие вопросы по тегам