C # CA2000: избавьтесь от объектов перед тем, как потерять область видимости, используя FileStream / XmlTextReader - PullRequest
8 голосов
/ 27 июня 2010

У меня много такого кода:

FileStream fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open); 
using (XmlTextReader reader = new XmlTextReader(fs)) 
{ 
   /* Some other code */
}

Это дает мне следующее предупреждение анализа кода:

CA2000 : Microsoft.Reliability : In method 'SF_Tester.Run()', object 'fs' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'fs' before all references to it are out of scope.

Если я последую предложению и добавлю File.Open в оператор using, я получу следующее:

CA2202 : Microsoft.Usage : Object 'fs' can be disposed more than once in method 'SF_Tester.Run()'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 39

Я использую VS2010 и не могу не думать, что делаю что-то не так, но не вижу этого. Что я делаю не так?

Ответы [ 7 ]

15 голосов
/ 27 июня 2010

Вздох, утомительно, не так ли? Избегайте всего этого, используя рекомендуемый метод Create ():

 using (var reader = XmlReader.Create(@"C:\Temp\SNB-RSS.xml")) {
     //...
 }
11 голосов
/ 13 сентября 2010

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

FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite,    FileShare.ReadWrite);
try
{
   using (var fileWriter = new StreamWriter(fs, encoding))
   {
       fs = null;
       fileWriter.Write(content);
    }
 }
 finally
 {
     if (fs != null)
         fs.Dispose();
 }

Это удаляет CA2000.

1 голос
/ 18 августа 2010

Это известная проблема

http://connect.microsoft.com/VisualStudio/feedback/details/535118/ca2000-and-ca2202-offer-contradictory-warnings

Если вы используете StreamWriter, а не XmlTextReader (как в решении выше), вы можете использовать аналогичный метод через соответствующий конструктор; например,

var sw = new StreamWriter("filename.txt");

или

var sw = new StreamWriter("filename.txt", /*append to file = */ false );

Из документации не ясно, будет ли первая форма конструктора перезаписывать или добавлять в файл.

1 голос
/ 27 июня 2010

Я только догадываюсь;у меня нет времени, чтобы пройти полный анализ сейчас.

Предположим, что конструктор XmlTextReader «берет на себя ответственность» за переданный поток, и поэтому удаление XmlTextReader также будет Dispose базовым потоком,Это объясняет поведение, которое вы видите.Возможно, конструктор XmlTextReader может выдать, и в этом случае будет иметь смысл исходное предупреждение о fs.Однако, учитывая эту гипотезу, этот код

        var fs = File.Open(@"C:\Temp\SNB-RSS.xml", FileMode.Open);
        XmlTextReader reader = null;
        try
        {
            reader = new XmlTextReader(fs);
        }
        finally
        {
            if (reader== null)
            {
                fs.Dispose();
            }
        }
        if (reader != null)
        {
            using (reader)
            {
                /* Some other code */
            }
        }

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

Как сказал кто-то еще, существует другой API для непосредственного создания считывателя из имени файла (XmlReader.Create()), который избегает всего этого (ипоказывает, насколько хорошо спроектированные API, ориентированные на сценарии, полезны по удивительным причинам).

0 голосов
/ 01 декабря 2014

просто используйте 'using' для файлового потока

 using(FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// some codes here

}

Не изменяйте fs и не используйте fs.close () внутри с помощью фигурных скобок.

0 голосов
/ 03 января 2013

Как уже упоминалось в этом ответе, единственный способ правильно обойти это - сделать , как рекомендовано в CA2202 и использовать внешний блок try-finally вместо внешнего блока using,Внутри внутреннего использования установите для внешнего объекта IDisposable значение null, чтобы предотвратить доступ к нему после завершения внутреннего использования.

Вот обобщенная оболочка, которая делает это «правильно», то есть работает с плохо спроектированным XmlReader (может быть, он не должен был вступать во владение потоком, который он получает? Не уверен, каким будет правильный способ сделать это)

Отказ от ответственности : Не проверено

public static TResult SafeNestedUsing<TOuter, TInner, TResult>(Func<TOuter> createOuterDisposable, Func<TOuter, TInner> createInnerDisposable, Func<TInner, TResult> body)
        where TInner : IDisposable
        where TOuter : class, IDisposable
    {
        TOuter outer = null;
        try
        {
            outer = createOuterDisposable();
            using (var inner = createInnerDisposable(outer))
            {
                var result = body(inner);
                outer = null;
                return result;
            }
        }
        finally
        {
            if (null != outer)
            {
                outer.Dispose();
            }
        }
    }

Пример использования:

SafeNestedUsing<MemoryStream, XmlReader, XmlDocument>(
    ()          => new MemoryStream(array),
    (memStream) => XmlReader.Create(memStream, xmlReaderSettings),
    (xmlReader) =>
    {
        XmlDocument xmlDoc = new XmlDocument();
        xmlDoc.Load(xmlReader);
        return xmlDoc;
    });

Это довольно довольно неуклюже, и вы можете утверждать, что вместо этого лучше повторить попытку try / set null / finally.Но для повторяющегося паттерна вложенных использований я бы предпочел сделать это таким образом, чем повторять все это каждый раз.

0 голосов
/ 27 июня 2010

Используйте оператор using также для самого FileStream, как и для XmlTextReader.

http://msdn.microsoft.com/en-us/library/system.io.filestream(VS.71).aspx.

Grz, Kris.

...