Code Anlysis Rule CA2000 / CA2202

Я пытаюсь убедиться, что мое кодирование соответствует правильному удалению объектов, поэтому я применяю эти правила как ошибки. Но у меня возникли проблемы с этим разделом кода

<code>using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

class MyClass
{  
    public String ToXml()
    {
        var objSerializer = 
            new DataContractSerializer(GetType());
        var objStream = new MemoryStream();
        StreamReader objReader;

        String strResult;
        try
        {
            // Serialize the object
            objSerializer.WriteObject(objStream, this);

            // Move to start of stream to read out contents
            objStream.Seek(0, SeekOrigin.Begin);

            objReader = new StreamReader(objStream);

            try
            {
                // Read Contents into a string
                strResult = objReader.ReadToEnd();
            }
            finally
            {
                objReader.Dispose();
            }
        }
        finally
        {
            if (objStream != null)
            {
                // objStream.Dispose();
            }
        }

        return strResult;
    }
}
</code>

Если я закомментируюobjStream.Dispose() Я получаю CA2000, поскольку я не удаляю объект, но если я удаляю комментарий, он говорит, что я удаляю несколько раз.

Что еще выбрасывает объект? или я просто делаю это неправильно при работе с несколькими потоками?

 Dreamwalker13 апр. 2012 г., 13:21
Я согласен, и это было изначально, я много баловался с этим, чтобы избавиться от ошибки
 Albin Sunnanbo13 апр. 2012 г., 13:17
Для удобства чтения я предпочитаю использовать блоки вместо явныхtry-finally{dispose}

Ответы на вопрос(3)

Решение Вопроса

Этот сценарий и сейчас меня раздражает. Каждые пару лет я решаю обновлять себя по правилам анализа кода. запустив fxcop или встроенный анализ кода в Visual Studio.

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

using (MemoryStream msDecrypt = new MemoryStream())
{
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
    {
        csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    }

    decrypted = msDecrypt.ToArray();
}

Этот блок кода приводит кCA2202 "Do not dispose objects multiple times" Большая ирония этого правила заключается в том, что речь идет не о проблеме с вашим кодом, а о том, что он защищает вас от проблемы в чужом коде. Microsoft всегда имела приличное количество документации о том, как шаблон Dispose (http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx) должен быть реализован. Однако, глядя на детали этого правила анализа кода (http://msdn.microsoft.com/en-us/library/ms182334.aspx) оно раскрывает цель этого правила

"A correctly implemented Dispose method can be called multiple times without throwing an exception. However, this is not guaranteed and to avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object."

Короче говоря, это правило касается защиты от людей, которые не следуют этим правилам.

Естественно, я изменил код, чтобы он выглядел так:

MemoryStream msDecrypt = new MemoryStream()    
//using (MemoryStream msDecrypt = new MemoryStream())
//{
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
    {
        csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    }

    decrypted = msDecrypt.ToArray();
//}

Теперь все в этом посте переполнения стека болезненно знают о новой проблеме, наш другCA2000 "Dispose objects before losing scope" ... так что на данный момент я просто лицо ладонью на минуту. Сделал несколько поисков в Google, и нашел этот пост. Тогда, когда меня осенило, чтобы перейти к обоим правилам CA, вы должны убедиться, что все размещено один раз и только один раз для всех ветвей кода. Поэтому я решил сделать это, что не является сложной проблемой, как только вы поймете, что это то, что вам нужно сделать.

Естественно, код эволюционировал к этому:

MemoryStream msDecrypt = null;
CryptoStream csDecrypt = null;

try
{    
    msDecrypt = new MemoryStream();
    csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);

    csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    csDecrypt.FlushFinalBlock();
    decrypted = msDecrypt.ToArray();
}
finally
{
    if (csDecrypt != null)
    {
        csDecrypt.Dispose();
    }
    else if (msDecrypt != null)
    {
        msDecrypt.Dispose();
    }

}

Наконец, у меня был код, который не привел к CA2000 или CA2202. Мораль этой истории заключается в том, что утверждение USING намного менее ценно, чем в прошлом, когда правила анализа кода развивались таким образом.

Есть несколько разных способов написания кода, чтобы сделать эту работу, я просто выбрал способ, который не смешивает явные вызовы для удаления с использованием операторов, потому что я считаю, что это проще для чтения, чем для структурирования. это помешает кому-то просто пойти и обернуть другого, используя его, что приведет к неосознанной первоначальной проблеме.

 22 окт. 2015 г., 10:21
Хотя это технически верно, если смотреть на это в общем, реальное использование мира не представляет никаких проблем с использованием "quat;" построить (т. е. когда удачно настроен дважды). Поэтому для удобства чтения я бы правильно подавил эту ошибку ТОЛЬКО, когда узнал, что класс обрабатывает ее правильно. Я думаю, что в этом смысл предупреждения анализа кода MS (чтобы мы думали об этом). Лучшим решением было бы, чтобы механизм правил анализа кода обнаружил это сам; чтобы дать нам какой-то способ объявить это (например, контракты кода). Я все равно проголосовал за твой ответ, так как это хорошее объяснение и его нужно рассмотреть.

Это не ответ, но вы можете найти этот код более читабельным:

public String ToXml()
{
    var objSerializer =
        new DataContractSerializer(GetType());

    using (var objStream = new MemoryStream())
    {
        //  Serialize the object
        objSerializer.WriteObject(objStream, this);

        // Move to start of stream to read 
        // out contents
        objStream.Seek(0, SeekOrigin.Begin);

        using (var objReader =
            new StreamReader(objStream))
        {
            // Read Contents into a string
            retirm objReader.ReadToEnd();
        }
    }
}
 16 июл. 2012 г., 12:12
Это именно то, что НЕ должно быть сделано в соответствии сmsdn.microsoft.com/en-us/library/ms182334 потому что Dispose () для объекта objStream может / будет вызываться дважды.
 16 июл. 2012 г., 12:15
С другой стороны: Джон Скит говорит, что все в порядке - так по закону.stackoverflow.com/a/1065196/40853
 16 июл. 2012 г., 17:27
Многократный вызов Dispose никогда не должен вызывать проблем (кроме, возможно, производительности), так какIDisposable В контракте говорится, что реализация должна быть в состоянии справиться с этим. В документации MSDN уже упоминается "Правильно реализованный метод Dispose может вызываться несколько раз без исключения." В этом смысле правило CA2202 очень странное, или, по крайней мере, его следует сгруппировать в категорию Microsoft.Performance вместо Microsoft.Usage.

Если вы располагаете StreamReader, вы также удаляете основной поток.

Если вы закомментируете objStream.Dispose (), то у вас появится шанс, что что-то вызовет исключение, прежде чем вы даже попадете во вложенный блок try - что приведет к тому, что ваш поток не будет ликвидирован.

Здесь хорошее объяснение: Утилизирует ли Streamreader поток?

 13 апр. 2012 г., 13:29
@ Зондер Да, это именно то, что я говорю. если вы располагаете читателем (или писателем), то вы также удаляете основной поток. таким образом, в вашем коде (без учета последней утилизации) он все равно заканчивается удалением дважды.
 13 апр. 2012 г., 13:31
@ Стивен, я согласен. и его сложно спроектировать вокруг него. Я обычно заканчиваю тем, что выбрасываю и поток и читатель, который чувствует себя излишним. представьте, если я добавлю писателя в микс.
 Dreamwalker13 апр. 2012 г., 13:22
Да, я понимаю, что если вы попробуете код, вы заметите, что вы дважды удаляете objStream, но в моем собственном коде это не так. Это случай StreamReader.Dispose, возможно, удаляет переданный в потоке?
 13 апр. 2012 г., 13:23
ИМО это странный дизайн причудStreamReader и это часто беспокоит меня, потому что иногда вы не хотите закрывать предоставленный поток.
 Dreamwalker13 апр. 2012 г., 14:10
хм да ок, так что довольно неудобно иметь оба эти правила анализа кода как ошибки. Единственный способ обойти это - сделать CA2202 предупреждением и надеяться, что программист не забудет перехватить ObjectDisposedException. Возможно, IDisposed должен иметь свойство IsDisposed, но тогда у вас есть проблемы с асинхронным удалением.

Ваш ответ на вопрос