次のコードからすべての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:オブジェクト 'cryptoStream'は、メソッド 'CryptoServices.Encrypt(string、byte []、byte [])'で複数回破棄できます。 System.ObjectDisposedExceptionの生成を回避するには、オブジェクトに対してDisposeを複数回呼び出さないでください。:行:34
警告8 CA2202:Microsoft.Usage:オブジェクト「memoryStream」は、メソッド「CryptoServices.Encrypt(string、byte []、byte [])」で複数回破棄できます。 System.ObjectDisposedExceptionの生成を避けるために、オブジェクトに対してDisposeを複数回呼び出さないでください。:行:34、37
これらの警告を表示するには、Visual Studio Code Analysisが必要です(これらはc#コンパイラの警告ではありません)。
この場合、警告を抑制する必要があります。ディスポーザブルを扱うコードは一貫している必要があり、作成したディスポーザブルの所有権を他のクラスが取得し、それらに対してDispose
を呼び出すことを気にする必要はありません。
[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();
}
}
UPDATE:IDisposable.Dispose ドキュメントでは、これを読むことができます:
オブジェクトのDisposeメソッドが複数回呼び出された場合、オブジェクトは最初の呼び出し以降のすべての呼び出しを無視する必要があります。オブジェクトは、Disposeメソッドが複数回呼び出された場合に例外をスローしてはなりません。
このルールは、私が上に示されているよう開発者は、using
使い捨てのカスケードでsanely文を採用することができる(または多分これは単なるニースの副作用である)ので、存在していることを主張することができます。同様に、CA2202は有用な目的を果たさないため、プロジェクトごとに抑制する必要があります。本当の犯人はDispose
の不完全な実装であり、 CA1065 はそれを処理する必要があります(あなたの責任の下にある場合)。
まあ、それは正確です。これらのストリームのDispose()メソッドは複数回呼び出されます。 StreamReaderクラスは、cryptoStreamの「所有権」を取得するため、streamWriterを破棄すると、cryptoStreamも破棄されます。同様に、CryptoStreamクラスがmemoryStreamの責任を引き継ぎます。
これらは実際のバグではなく、これらの.NETクラスは複数のDispose()呼び出しに対して回復力があります。ただし、警告を削除する場合は、これらのオブジェクトのusingステートメントを削除する必要があります。そして、コードが例外をスローした場合に何が起こるかを推論するとき、少し苦労します。または、属性を使用して警告を閉じます。それともばかげているので、警告を無視してください。
StreamWriter が破棄されると、ラップされた Stream (ここでは CryptoStream )が自動的に破棄されます。 CryptoStream は、ラップされたものを自動的に破棄します Stream (ここでは MemoryStream )。
したがって、 MemoryStream は CryptoStream とusingステートメントの両方によって破棄されます。そして CryptoStream は StreamWriter および外側のusingステートメントによって破棄されます。
いくつかの実験の後、警告を完全に取り除くことは不可能のようです。理論的には、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();
#pragma warning disable
を使用してこれを行います。
.NET Frameworkガイドラインでは、IDisposable.Disposeを複数回呼び出せるように実装することを推奨しています。 IDisposable.DisposeのMSDNの説明 から:
オブジェクトは、Disposeメソッドが複数回呼び出された場合に例外をスローしてはなりません
したがって、警告はほとんど意味がないようです。
System.ObjectDisposedExceptionの生成を回避するには、オブジェクトに対してDisposeを複数回呼び出さないでください。
標準の実装ガイドラインに従っていない、実装が不十分なIDisposableオブジェクトを使用している場合、警告が役立つ可能性があると考えることができます。しかし、あなたのように.NET Frameworkのクラスを使用する場合は、#pragmaを使用して警告を抑制しても安全だと思います。そして、これは この警告についてはMSDNのドキュメントで提案されている のようにフープを通過するよりも望ましいです。
コードで同様の問題に直面しました。
コンストラクター(CA2000)で例外が発生した場合、MemoryStream
を破棄できるため、CA2202全体がトリガーされたように見えます。
これは次のように解決できます。
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 }
memoryStream
は11行目で破棄されるため(using
で使用されているため)、最後のcryptoStream
ステートメント内に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();
}
}
}
}
すべての使用を避け、ネストされた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クラスは、.Dispose
への複数の呼び出しの間違いに対して(できれば)弾力性がありますが、allクラスではなく、プログラマの誤用に対する防御です。
FX Copはこれを知っており、警告します。
いくつかの選択肢があります。
Dispose
を1回だけ呼び出します。 using
を使用しないでくださいトピック外ですが、using
sをグループ化するために別のフォーマット手法を使用することをお勧めします。
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
sを使用して、本当に長いクラス名の繰り返しを避けることを推奨します。
追伸指摘してくれた@Shellshockのおかげで、using
in memoryStream
ステートメントが範囲外になるため、最初のreturn
の括弧を省略できません。