[c#] 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 : ‘CryptoServices.Encrypt (string, byte [], byte [])’메서드에서 ‘cryptoStream’개체를 두 번 이상 삭제할 수 있습니다. System.ObjectDisposedException을 생성하지 않으려면 개체에 대해 Dispose를 두 번 이상 호출하면 안됩니다. : 줄 : 34

경고 8 CA2202 : Microsoft.Usage : ‘CryptoServices.Encrypt (string, byte [], byte [])’메서드에서 ‘memoryStream’개체를 두 번 이상 삭제할 수 있습니다. System.ObjectDisposedException을 생성하지 않으려면 개체에 대해 Dispose를 두 번 이상 호출하면 안됩니다. : 줄 : 34, 37

이러한 경고를 보려면 Visual Studio Code Analysis가 필요합니다 (이는 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폐기 책임이 다른 객체로 이전되는 즉시 참조가로 설정됩니다 . 예를 들어 생성자 호출이 성공한 후로 memoryStream설정됩니다 . 생성자 호출이 성공한 후로 설정됩니다 . 예외가 발생하지 않으면, 배치되어 회전 처분의 블록을 뜻 하고 .nullCryptoStreamcryptoStreamnullStreamWriterstreamWriterfinallyCryptoStreamMemoryStream


답변

이 경우 경고를 표시하지 않아야합니다. 일회용품을 처리하는 코드는 일관성이 있어야하며 다른 클래스가 생성 한 일회용품의 소유권을 가져 와서 호출 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();
  }
}

업데이트 : 에서 IDisposable.Dispose의 문서 당신이 이것을 읽을 수 있습니다 :

개체의 Dispose 메서드가 두 번 이상 호출되는 경우 개체는 첫 번째 호출 이후의 모든 호출을 무시해야합니다. Dispose 메서드가 여러 번 호출되는 경우 개체는 예외를 throw하지 않아야합니다.

이 규칙이 존재한다고 주장 할 수 있는데, 개발자가 using위에서 본 것처럼 일련의 일회용품에서 성명을 건전하게 사용할 수 있습니다 (또는 이것은 좋은 부작용 일 수도 있습니다). 마찬가지로 CA2202는 유용한 용도로 사용되지 않으며 프로젝트 차원에서 억제되어야합니다. 실제 범인은 잘못된 구현 Dispose이며 CA1065가이를 처리 해야합니다 (귀하의 책임하에있는 경우).


답변

정확합니다. 이러한 스트림의 Dispose () 메서드는 두 번 이상 호출됩니다. StreamReader 클래스는 cryptoStream의 ‘소유권’을 가져 오므로 streamWriter를 폐기하면 cryptoStream도 폐기됩니다. 마찬가지로 CryptoStream 클래스가 memoryStream에 대한 책임을 맡습니다.

이는 실제 버그가 아닙니다. 이러한 .NET 클래스는 여러 Dispose () 호출에 탄력적입니다. 그러나 경고를 제거하려면 이러한 개체에 대한 using 문을 삭제해야합니다. 그리고 코드에서 예외가 발생하면 어떤 일이 발생할지 추론 할 때 약간의 고통을 겪습니다. 또는 속성으로 경고를 종료하십시오. 또는 어리석기 때문에 경고를 무시하십시오.


답변

StreamWriter 가 삭제 되면 래핑 된 Stream (여기서는 CryptoStream ) 이 자동으로 삭제됩니다 . CryptoStream 은 또한 래핑 된 Stream (여기서는 MemoryStream )을 자동으로 삭제합니다 .

따라서 MemoryStreamCryptoStreamusing 문에 의해 삭제됩니다. 그리고 CryptoStreamStreamWriter 와 외부 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 메서드가 여러 번 호출되는 경우 개체는 예외를 throw하지 않아야합니다.

따라서 경고는 거의 의미가없는 것 같습니다.

System.ObjectDisposedException 생성을 방지하려면 개체에서 Dispose를 두 번 이상 호출하지 않아야합니다.

표준 구현 지침을 따르지 않는 잘못 구현 된 IDisposable 개체를 사용하는 경우 경고가 도움이 될 수 있다고 주장 할 수 있습니다. 그러나 .NET Framework의 클래스를 사용하는 것처럼 #pragma를 사용하여 경고를 억제하는 것이 안전하다고 말하고 싶습니다. 그리고 IMHO는 이 경고에 대한 MSDN 문서에 제안 된 대로 후프를 거치는 것보다 낫습니다 .


답변

내 코드에서 비슷한 문제에 직면했습니다.

MemoryStream생성자 (CA2000)에서 예외가 발생하면 폐기 될 수 있기 때문에 전체 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 }

11 번째 줄에서 처리되기 때문에 ( 문 에서 사용되기 때문에 ) memoryStream마지막 using문 (10 번째 줄) 내부 를 반환해야하며 , 11 번째 줄에서도 처리 됩니다 (를 만드는 데 사용 되기 때문에 ).cryptoStreamstreamWriter usingmemoryStreammemoryStreamcryptoStream

적어도이 코드는 나를 위해 일했습니다.

편집하다:

재미있게 들릴지 모르지만 GetMemoryStream다음 코드로 메서드 를 대체하면

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

동일한 결과를 얻습니다.


답변

cryptostream은 메모리 스트림을 기반으로합니다.

일어나는 것처럼 보이는 것은 크라이 포스트 림이 (사용 종료시) 폐기 될 때 메모리 스트림도 폐기되고, 그 다음 메모리 스트림이 다시 폐기된다는 것입니다.