Утилизировать объект более одной ошибки времени. CA2202. Есть ли способ лучше?

Как я могу убедиться, что следующий код лучше удаляет все объекты? В настоящее время анализ кода говорит мне

Ошибка 45 CA2202: Microsoft.Usage: объект «ns» может быть удален более одного раза в методе «CPCommunicator.GetResults (строка)». Чтобы избежать генерации System.ObjectDisposedException, вы не должны вызывать Dispose более одного раза для объекта.: Строки: 64, 65

NetworkStream ns = null;
StreamWriter requestStream = null;
TextReader responseStream = null;

var results = new StringBuilder();

try
{
    ns = new NetworkStream(CreateConnection(), true);
    requestStream = new StreamWriter(ns);
    requestStream.Flush();
    responseStream = new StreamReader(ns);

    requestStream.Write(reportData);
    requestStream.Flush();
    while (responseStream.Peek() != -1)
    {
        var currentLine = responseStream.ReadLine();
        results.Append(currentLine);
        results.Append("\n");
    }
}
finally
{
    if (requestStream != null) requestStream.Close();
    if (responseStream != null) responseStream.Close();
    if (cpNetworkStream != null) cpNetworkStream.Close();
}

Поскольку и requestStream, и responseStream используют ns, они оба избавляются от ns, поэтому, чтобы удовлетворить предупреждение анализа кода, я должен закомментировать два последних метода close в блоке finally. Но действительно ли я хочу этого?????


person Chris Conway    schedule 10.05.2010    source источник


Ответы (2)


Да, имхо, вы действительно должны вызывать его только один раз.

В качестве альтернативы вы можете использовать синтаксис using для ns, что еще больше проясняет ситуацию.

using (ns = new NetworkStream(CreateConnection(), true)) {
   ...
}
person Foxfire    schedule 10.05.2010
comment
правильно, но я думаю, что проблема связана с попыткой закрыть requestStream и responseStream. Я завернул все три в операторы использования, но все равно получаю ошибку анализа кода. после того, как requestStream закрывается и удаляется, вызов close в responseStream также пытается закрыть ns, и это то, что CA кричит на меня. - person Chris Conway; 10.05.2010
comment
Вы не должны оборачивать все три в использование, а только ns, которое есть в моем ответе. - person Foxfire; 10.05.2010
comment
Вы предлагаете мне не закрывать requestStream или responseStream, а только networkStream? обертывание только ns в операторе using и закрытие двух других вручную по-прежнему дает мне ошибку. - person Chris Conway; 10.05.2010
comment
Да, не стоит закрывать остальные. Их закрытие просто закрывает основной поток. А в вашем случае вы уже знаете, что этот поток гарантированно будет закрыт. - person Foxfire; 10.05.2010
comment
не вызовет ли открытый стример или текстовый ридер утечку памяти? - person Chris Conway; 11.05.2010
comment
Отсутствие закрытия/удаления объекта НИКОГДА не приводит к утечке памяти. Вся память по-прежнему будет собираться мусором. В вашем случае это вообще не имеет значения. Единственным потенциальным недостатком (отсутствующим в вашем случае) может быть то, что если вы не вызываете close/dispose, может потребоваться две сборки мусора (поскольку в первой коллекции вызывается только финализатор), пока память не будет освобождена. - person Foxfire; 11.05.2010
comment
@Foxfire: Заявление о том, что отсутствие удаления объекта никогда не приведет к утечке памяти, немного вводит в заблуждение. Отсутствие удаления никогда не приведет к утечке управляемой памяти, но вполне возможно, что объект будет использовать неуправляемую память, а затем освободить эту память, если/когда она будет удалена. - person LukeH; 14.05.2010
comment
@LukeH: даже если он использует неуправляемую память, эта память все равно будет освобождена, как только управляемый объект будет собран (или на одну коллекцию позже). Разница лишь в том, что это произойдет позже, чем если вы вызовете Close (тогда это произойдет сразу). Но эта память не будет утеряна. Если разработчик не вызвал dispose, то GC вызовет его во время сбора. - person Foxfire; 15.05.2010
comment
@Foxfire: GC никогда не вызывает Dispose. GC в конечном итоге вызовет финализатор объекта, поэтому объект IDisposable может предоставить резервный финализатор, который очищается, если разработчик забывает удалить, но этот финализатор не является частью контракта IDisposable (даже если это хорошая практика) . - person LukeH; 16.05.2010
comment
@LukeH: Вы правы, что он не вызывает Dispose напрямую. Но, как вы написали, он вызывает финализатор, а финализатор, в свою очередь, вызывает Dispose (по крайней мере, для всех классов фреймворка). Так что косвенно он ВСЕГДА вызывает Dispose. Более того, GC не вызовет его в конце концов, но на самом деле гарантируется, что он будет вызван. Просто время, когда это произойдет, не гарантируется. - person Foxfire; 17.05.2010

Я бы реорганизовал ваш код так:

using (NetworkStream ns = new NetworkStream(CreateConnection(), true))
using (StreamWriter requestStream = new StreamWriter(ns))
using (TextReader responseStream = new StreamReader(ns))
{

    var results = new StringBuilder();

    requestStream.Flush();

    requestStream.Write(reportData);
    requestStream.Flush();
    while (responseStream.Peek() != -1)
    {
        var currentLine = responseStream.ReadLine();
        results.Append(currentLine);
        results.Append("\n");
    }
}
person Skywalker    schedule 10.05.2010
comment
хотя, конечно, чище, я все равно получаю одно и то же сообщение о том, что не вызываю dispose для ns более одного раза для объекта. Ошибка 45 CA2202: Microsoft.Usage: объект «ns» может быть удален более одного раза в методе «Communicator.GetResults(string)». Чтобы избежать создания System.ObjectDisposedException, вы не должны вызывать Dispose для объекта более одного раза. - person Chris Conway; 10.05.2010
comment
Этот ответ имеет ту же проблему, что и исходный вопрос. - person Foxfire; 10.05.2010