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
块中,并依次处理CryptoStream
和MemoryStream
。
在这种情况下,你应该压制警告。 处理一次性用品的代码应该是一致的,你不应该在意其他类别拥有你所创建的一次性用品的所有权,也可以对其进行调用。
[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应该照顾(如果这是你的责任)。
好吧,这些流的Dispose()方法将被调用多次。 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
括号,因为它会使memoryStream
在return
语句超出范围。
我在我的代码面临类似的问题。
看起来像整个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
- 继续调用两次,希望代码不会崩溃
- 压制警告