2010-06-27 2 views
8

У меня есть много кода, как это:C# CA2000: Утилизировать объекты до потери сферы с помощью FileStream/XmlTextReader

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 в использовании заявление, я получаю это:

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 и Я не могу не думать, что я делаю что-то неправильно, но я этого не вижу. Что я делаю неправильно?

+0

FYI, «новый XmlTextReader» устарел с .NET 2.0. Вместо этого используйте 'XmlReader.Create', как показывает Ганс ниже. –

+0

Jeepers ... Я не знал, что XmlTextReader устарел. Теперь это имеет гораздо больший смысл. Благодаря! –

+0

Причина 'CA2202' в том, что утилизация' Reader' или 'Writer' также избавляет от' FileStream', используемых для его создания; это означает, что вы должны распоряжаться 'fs', если построение' Writer' терпит неудачу, но если это не удастся; что приводит к [ниже] (http://stackoverflow.com/a/3700444/562906) ** testalino **. – PJTraill

ответ

15

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

using (var reader = XmlReader.Create(@"C:\Temp\SNB-RSS.xml")) { 
    //... 
} 
1

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

Предположим, что конструктор 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()), что позволяет избежать этого (и показывает, насколько хорошо разработаны сценарии, ориентированные на сценарии, являются хорошей вещью для удивительного множества причин).

1

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

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

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

11

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

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.

+0

Хорошо, все утро искали, и это первый подход, который действительно сработал. Благодаря! Моя проблема была в StreamWriter, а не в XMLReader, но тот же CA Hell точно такой же. – TheZenker

+0

На самом деле это лучший ответ. См. Http://connect.microsoft.com/VisualStudio/feedback/details/611525/disposable-objects-and-ca2000-ca2202-warnings – ken2k

+0

** testalino **: вы можете интегрировать это в свой ответ: причина что 'использование fs', как в вопросе вызывает' CA2202', заключается в том, что утилизация 'Reader' или' Writer' также уничтожает 'FileStream', используемый для ее создания; это означает, что вам нужно распоряжаться 'fs', если построение' Writer' терпит неудачу, но если это не удастся! Это кажется мне недостатком в дизайне этих классов. Я не могу сказать, что мне нравится использовать вашу модель, но я не вижу лучшего подхода; Написание классов оболочки кажется неправильным. – PJTraill

0

Как уже упоминалось в сообщении this, единственный способ правильно его обработать - сделать as recommended in CA2202 и использовать внешний блок try-finally вместо внешнего блока. Внутри внутреннего использования установите для внешнего объекта 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

просто использовать «с помощью» для FileStream

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

} 

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

 Смежные вопросы

  • Нет связанных вопросов^_^