代码分析规则CA2000 / CA2202

我正在努力确保我的编码遵循正确的对象处理方式,因此我将这些规则强制执行为错误。 但我在使用这段代码时遇到了麻烦

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

如果我注释掉objStream.Dispose()我得到了CA2000,因为我没有处理该对象,但是如果我删除了注释,那么我说我处理了不止一次。

还有什么处理对象? 或者我在处理多个流时只是做错了吗?

这个场景现在也让我烦恼。 每隔几年,我决定通过在Visual Studio中运行fxcop或现在的内置代码分析来刷新自己的代码分析“规则”。

我最初编写这段代码,认为我是一个善于使用uses处理的好公民:

 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“不要多次丢弃对象”这个规则的讽刺之处在于,它并不是关于代码的问题,而是保护您关于其他代码中的问题。 微软一直有大量关于如何实现Dispose模式( http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx )的文档。 但是,通过查看此代码分析规则( http://msdn.microsoft.com/en-us/library/ms182334.aspx )的详细信息,它揭示了此规则的用途

“可以多次调用正确实现的Dispose方法而不抛出exception。但是,这不能保证,并且为了避免生成System.ObjectDisposedException,不应该在对象上多次调用Dispose。”

简而言之,这条规则就是保护自己免受那些不遵守规则的人的伤害。

当然我修改了代码看起来像这样:

 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“在失去范围之前处理对象” ……所以在这一点上我只是面对了一分钟。 做过几次谷歌搜索,发现这篇文章。 就在我突然想到要传递两个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语句的价值远低于过去。

有几种不同的方法可以编写代码来实现这个function,我只是选择了一种不会将显式调用与dispose混合使用的方法,因为我认为这样做更简单易读和结构化这会阻止某人在周围使用它来包装另一个人,导致最初的问题在不知不觉中发生。

如果丢弃StreamReader,则还要处理基础流。

如果你注释掉objStream.Dispose(),那么在你进入嵌套的try块之前你会遇到抛出exception的可能性 – 这将导致你的流没有被处理掉。

这里有一个很好的解释: 处理streamreader会关闭流吗?

这不是答案,但您可能会发现此代码更具可读性:

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