[英]C# CA2000:Dispose objects before losing scope using 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放在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而且我不禁想到我做錯了什么但是我沒有看到它。 我究竟做錯了什么?
嘆氣,筋疲力盡吧。 通過使用推薦的Create()方法避免所有這些:
using (var reader = XmlReader.Create(@"C:\Temp\SNB-RSS.xml")) {
//...
}
由於沒有人提供解決此問題的解決方案,我正在編寫我的工作解決方案:
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。
我猜只是; 現在沒有時間進行全面分析。
假設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是出於各種各樣的原因,這是一件好事。 )。
這是一個眾所周知的問題
如果您使用的是StreamWriter而不是XmlTextReader(如上面的解決方案中所示),您可以通過相關的構造函數使用類似的方法; 例如
var sw = new StreamWriter("filename.txt");
要么
var sw = new StreamWriter("filename.txt", /*append to file = */ false );
從文檔中不清楚第一種形式的構造函數是否會覆蓋或附加到文件。
正如本答案中所提到的,正確解決它的唯一方法是按照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;
});
這是相當笨重,你可能會說,這是更好地重復嘗試/套空/最后的圖案代替。 但是對於嵌套使用的重復模式,我寧願這樣做,也不是每次重復完整的事情。
只需使用'using'作為文件流
using(FileStream fs = new FileStream(fileName, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite))
{
// some codes here
}
不要修改fs,也不要在花括號內部使用fs.close()。
也可以像在XmlTextReader上一樣在FileStream上使用using
語句。
http://msdn.microsoft.com/en-us/library/system.io.filestream(VS.71).aspx 。
格茲,克里斯。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.