CA2202,如何解决这种情况

任何人都可以告诉我如何从下面的代码中删除所有的CA2202警告?

public static byte[] Encrypt(string data, byte[] key, byte[] iv) { using(MemoryStream memoryStream = new MemoryStream()) { using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider()) { using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write)) { using(StreamWriter streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(data); } } } return memoryStream.ToArray(); } } 

警告7 CA2202:Microsoft.Usage:Object'cryptoStream'可以在方法CryptoServices.Encrypt(string,byte [],byte [])'中多次使用。 为了避免产生System.ObjectDisposedException,你不应该在一个对象上多次调用Dispose:Lines:34

警告8 CA2202:Microsoft.Usage:Object'memoryStream'可以在方法CryptoServices.Encrypt(string,byte [],byte [])'中多次使用。 为了避免产生System.ObjectDisposedException,你不应该在一个对象上多次调用Dispose:Lines:34,37

您需要Visual Studio代码分析来查看这些警告(这些不是C#编译器警告)。

这个编译没有警告:

  public static byte[] Encrypt(string data, byte[] key, byte[] iv) { MemoryStream memoryStream = null; DESCryptoServiceProvider cryptograph = null; CryptoStream cryptoStream = null; StreamWriter streamWriter = null; try { memoryStream = new MemoryStream(); cryptograph = new DESCryptoServiceProvider(); cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write); var result = memoryStream; memoryStream = null; streamWriter = new StreamWriter(cryptoStream); cryptoStream = null; streamWriter.Write(data); return result.ToArray(); } finally { if (memoryStream != null) memoryStream.Dispose(); if (cryptograph != null) cryptograph.Dispose(); if (cryptoStream != null) cryptoStream.Dispose(); if (streamWriter != null) streamWriter.Dispose(); } } 

编辑回应评论:我只是再次验证,这个代码不会生成警告,而原来的一个。 在原始的代码中, CryptoStream.Dispose()MemoryStream().Dispose( )实际上被调用了两次(这可能是也可能不是问题)。

修改后的代码的工作原理如下:只要将处理的职责转移到另一个对象,引用就设置为null 。 例如,在对CryptoStream构造函数的调用成功之后, memoryStream被设置为null 。 在对StreamWriter构造函数的调用成功之后, cryptoStream被设置为null 。 如果没有发生异常, streamWriter被放置在finally块中,并依次处理CryptoStreamMemoryStream

在这种情况下,你应该压制警告。 处理一次性用品的代码应该是一致的,你不应该在意其他类别拥有你所创建的一次性用品的所有权,也可以对其进行调用。

 [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public static byte[] Encrypt(string data, byte[] key, byte[] iv) { using (var memoryStream = new MemoryStream()) { using (var cryptograph = new DESCryptoServiceProvider()) using (var cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write)) using (var streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(data); } return memoryStream.ToArray(); } } 

更新:在IDisposable.Dispose文档中,您可以阅读:

如果一个对象的Dispose方法被多次调用,则该对象必须在第一个调用之后忽略所有的调用。 如果Dispose方法被多次调用,则该对象不得抛出异常。

可以说这个规则是存在的,所以开发人员可以在一次性的一次性使用中正确地using语句,就像我上面所说的(或者这只是一个很好的副作用)。 因此,同样的道理,CA2202没有任何用处,应该以项目的方式予以抑制。 真正的罪魁祸首是Dispose的错误实施, CA1065应该照顾(如果这是你的责任)。

好吧,这些流的D​​ispose()方法将被调用多次。 StreamReader类将获得cryptoStream的“所有权”,所以配置streamWriter也会配置cryptoStream。 同样,CryptoStream类接管memoryStream的责任。

这些不完全是真正的错误,这些.NET类对多个Dispose()调用是有弹性的。 但是如果你想摆脱这个警告,那么你应该放弃这些对象的使用语句。 当推理如果代码抛出一个异常时会发生什么,会让自己痛苦一些。 或者用一个属性关闭警告。 或者只是忽略这个警告,因为它很愚蠢。

我会这样做使用#pragma warning disable

.NET Framework指南建议以可多次调用的方式实现IDisposable.Dispose。 从MSDN描述IDisposable.Dispose :

如果Dispose方法被多次调用,则该对象不得抛出异常

所以这个警告似乎几乎没有意义:

为避免生成System.ObjectDisposedException,您不应该在对象上多次调用Dispose

我想这可能会被认为是警告可能会有所帮助,如果您使用不符合标准的实施准则严重执行的IDisposable对象。 但是像.NET Framework一样使用.NET框架中的类时,我会说使用#pragma来压制警告是安全的。 恕我直言,这是最好的经历了这个警告的MSDN文档建议的箍。

当一个StreamWriter被处置时,它将自动处理被包装的Stream (这里: CryptoStream )。 CryptoStream也会自动处理被包装的Stream (这里是: MemoryStream )。

所以你的MemoryStream由CryptoStream和using语句处理。 而您的CryptoStream由StreamWriter和外部使用语句处理。


经过一番实验,似乎完全摆脱了警告是不可能的。 理论上,MemoryStream需要处理,但是理论上不能再访问它的ToArray方法。 实际上,一个MemoryStream不需要处理,所以我会采用这个解决方案,并抑制CA2000的警告。

 var memoryStream = new MemoryStream(); using (var cryptograph = new DESCryptoServiceProvider()) using (var writer = new StreamWriter(new CryptoStream(memoryStream, ...))) { writer.Write(data); } return memoryStream.ToArray(); 

密码流是基于内存流的。

看起来正在发生的事情是,当处置crypostream(在使用结束时)时,也配置了存储器流,然后将存储器流再次处理。

离题,但我建议你使用不同的格式化技术进行分组:

 using (var memoryStream = new MemoryStream()) { using (var cryptograph = new DESCryptoServiceProvider()) using (var encryptor = cryptograph.CreateEncryptor(key, iv)) using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) using (var streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(data); } return memoryStream.ToArray(); } 

我也主张在这里使用var来避免重复很长的类名。

PS感谢@ShellShock指出我不能省略首先using括号,因为它会使memoryStreamreturn语句超出范围。

我在我的代码面临类似的问题。

看起来像整个CA2202的东西被触发,因为如果在构造函数(CA2000)中发生异常,可以处置MemoryStream

这可以像这样解决:

  1 public static byte[] Encrypt(string data, byte[] key, byte[] iv) 2 { 3 MemoryStream memoryStream = GetMemoryStream(); 4 using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider()) 5 { 6 CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write); 7 using (StreamWriter streamWriter = new StreamWriter(cryptoStream)) 8 { 9 streamWriter.Write(data); 10 return memoryStream.ToArray(); 11 } 12 } 13 } 14 15 /// <summary> 16 /// Gets the memory stream. 17 /// </summary> 18 /// <returns>A new memory stream</returns> 19 private static MemoryStream GetMemoryStream() 20 { 21 MemoryStream stream; 22 MemoryStream tempStream = null; 23 try 24 { 25 tempStream = new MemoryStream(); 26 27 stream = tempStream; 28 tempStream = null; 29 } 30 finally 31 { 32 if (tempStream != null) 33 tempStream.Dispose(); 34 } 35 return stream; 36 } 

请注意,我们必须返回最后using语句(第10行)中的cryptoStream ,因为cryptoStream被放置在第11行(因为它在streamWriter using语句),导致memoryStream也被放置在第11行(因为memoryStream被用于创建cryptoStream )。

至少这个代码为我工作。

编辑:

听起来很有趣,我发现如果用下面的代码替换GetMemoryStream方法,

 /// <summary> /// Gets a memory stream. /// </summary> /// <returns>A new memory stream</returns> private static MemoryStream GetMemoryStream() { return new MemoryStream(); } 

你会得到相同的结果。

我想以正确的方式解决这个问题,那就是不要压制警告,正确处置所有的一次性物品。

我把3个流中的2个抽出为字段,并将它们放在我的类的Dispose()方法中。 是的,实现IDisposable接口可能不一定是你正在寻找,但解决方案看起来相当干净,与从代码中的所有随机地方dispose()调用相比。

 public class SomeEncryption : IDisposable { private MemoryStream memoryStream; private CryptoStream cryptoStream; public static byte[] Encrypt(string data, byte[] key, byte[] iv) { // Do something this.memoryStream = new MemoryStream(); this.cryptoStream = new CryptoStream(this.memoryStream, encryptor, CryptoStreamMode.Write); using (var streamWriter = new StreamWriter(this.cryptoStream)) { streamWriter.Write(plaintext); } return memoryStream.ToArray(); } public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { if (this.memoryStream != null) { this.memoryStream.Dispose(); } if (this.cryptoStream != null) { this.cryptoStream.Dispose(); } } } } 

我使用这种类型的代码,需要byte []并返回byte []而不使用流

 public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv) { DES des = new DES(); des.BlockSize = 128; des.Mode = CipherMode.CBC; des.Padding = PaddingMode.Zeros; des.IV = IV des.Key = key ICryptoTransform encryptor = des.CreateEncryptor(); //and finaly operations on bytes[] insted of streams return encryptor.TransformFinalBlock(plaintextarray,0,plaintextarray.Length); } 

这样你所要做的就是使用编码将字符串转换为字节[]。

避免所有使用和使用嵌套Dispose-Calls!

  public static byte[] Encrypt(string data, byte[] key, byte[] iv) { MemoryStream memoryStream = null; DESCryptoServiceProvider cryptograph = null; CryptoStream cryptoStream = null; StreamWriter streamWriter = null; try { memoryStream = new MemoryStream(); cryptograph = new DESCryptoServiceProvider(); cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write); streamWriter = new StreamWriter(cryptoStream); streamWriter.Write(data); return memoryStream.ToArray(); } finally { if(streamWriter != null) streamWriter.Dispose(); else if(cryptoStream != null) cryptoStream.Dispose(); else if(memoryStream != null) memoryStream.Dispose(); if (cryptograph != null) cryptograph.Dispose(); } } 

我只是想打开代码,所以我们可以看到多个调用Dispose的对象:

 memoryStream = new MemoryStream() cryptograph = new DESCryptoServiceProvider() cryptoStream = new CryptoStream() streamWriter = new StreamWriter() memoryStream.Dispose(); //implicitly owned by cryptoStream cryptoStream.Dispose(); //implicitly owned by streamWriter streamWriter.Dispose(); //through a using cryptoStream.Dispose(); //INVALID: second dispose through using cryptograph.Dispose(); //through a using memorySTream.Dipose(); //INVALID: second dispose through a using return memoryStream.ToArray(); //INVALID: accessing disposed memoryStream 

虽然大多数.NET类(希望)能够抵御多次调用的错误,但是并不是所有的类都可以防止程序员的错误使用。

FX Cop知道这一点,并警告你。

你有几个选择,

  • 只对任何对象调用Dispose一次; 不要using
  • 继续调用两次,希望代码不会崩溃
  • 压制警告