CA2202,이 사건을 해결하는 방법


102

아무도 다음 코드에서 모든 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 # 컴파일러 경고가 아님).


1
이 코드는 이러한 경고를 생성하지 않습니다.
Julien Hoarau

1
이에 대해 0 개의 경고가 표시됩니다 (경고 수준 4, VS2010). 이 영역에서 문제를 검색하는 사람을 위해 경고 텍스트도 추가합니다.
Henk Holterman

29
CAxxxx 경고는 Code Analysis 및 FxCop에서 생성됩니다 .
dtb 2014 년

이 경고는 표시된 코드에는 적용되지 않습니다.이 시나리오에서는 경고를 표시하지 않을 수 있습니다. 코드를 검토하고 평가에 동의하면 다음을 방법 위에 배치하십시오. " [SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times", Justification="BrainSlugs83 said so.")]" using System.Diagnostics.CodeAnalysis;-usings 블록에 " "문 이 있는지 확인하십시오 .
BrainSlugs83 2013

답변:


-3

이것은 경고없이 컴파일됩니다.

    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


85
-1 억제해야하는 경고를 준수하기 위해 추악한 코드를 만드는 것은 정말 나쁩니다 .
Jordão 2011-08-03

4
나는 당신이 미래의 어느 시점에서 고쳐질 수있는 코드를 도살해서는 안된다는 데 동의합니다.
peteski

3
이것이 문제를 어떻게 해결합니까? memoryStream은 finally 블록에서 두 번 삭제 될 수 있으므로 CA2202는 계속보고됩니다.
Chris Gessler 2012-06-23

3
CryptoStream은 MemoryStream에 대해 Dispose를 내부적으로 호출하므로 두 번 호출 될 수 있으므로 경고가 발생합니다. 나는 당신의 해결책을 시도했지만 여전히 경고를받습니다.
Chris Gessler 2012-06-23

2
오 이런, 당신 말이 맞아요-당신의 ... 논리 논리와 섞인 정리 논리가있을 거라고는 예상하지 못했어요 ...-그건 기괴하고 비밀 스럽죠-확실히 영리합니다-하지만 다시 말하지만, 무서운-프로덕션 코드에서는이 작업을 수행하지 마십시오. 명확하게 말하면, 이것이 해결되는 실제 기능적 문제가 없다는 것을 알 수 있습니다. 맞습니까? (이러한 개체를 여러 번 처리하는 것은 괜찮습니다.)-가능하면 반대표를 제거하겠습니다 (그러므로 저를 방지하고 답변을 편집해야한다고 말합니다)-하지만 마지 못해 그렇게 할뿐입니다 ...- 진지하게, 절대 이러지 마세요.
BrainSlugs83 2013

142

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


14
제 생각에는 이것은 fxcop의 버그이며,이 규칙은 단순히 잘못되었습니다. dispose 메서드는 ObjectDisposedException을 throw해서는 안되며, 발생하는 경우 이러한 방식으로 dispose를 구현하는 코드의 작성자에게 버그를 제출하여 처리해야합니다.
justin.m.chase 2014

14
다른 스레드의 @HansPassant에 동의합니다. 도구가 작업을 수행하고 클래스의 예상치 못한 구현 세부 사항에 대해 경고합니다. 개인적으로 진짜 문제는 API 자체의 디자인이라고 생각합니다. 중첩 클래스가 기본적으로 다른 곳에서 생성 된 다른 객체의 소유권을 가져도 좋다고 가정하는 것은 매우 의심스러운 것 같습니다. 결과 객체가 반환 될 경우 유용 할 수있는 위치를 알 수 있지만 해당 가정을 기본값으로 설정하는 것은 직관적이지 않고 정상적인 IDisposable 패턴을 위반하는 것처럼 보입니다.
BTJ 2014

8
그러나 msdn은 이러한 유형의 메시지를 일시 중지하도록 권장하지 않습니다. : 한 번 봐 가지고 msdn.microsoft.com/en-us/library/...
아딜 맘마 도프

2
@AdilMammadov, 유용한 정보 링크 주셔서 감사합니다. 그러나 Microsoft는 이러한 것들에 대해 항상 옳은 것은 아닙니다.
Tim Abell

40

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

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


10
클래스의 내부 동작에 대한 특별한 지식 (예 : 다른 클래스의 소유권을 가져 오는 일회용)은 재사용 가능한 API를 디자인 할 것인지 묻기에는 너무 많습니다. 그래서 나는 using진술이 유지되어야 한다고 생각합니다 . 이러한 경고는 정말 어리 석습니다.
Jordão 2015 년

4
@ Jordão-도구가 무엇을위한 것이 아닙니까? 당신이 알지 못했던 내부 행동에 대해 경고하기 위해?
Hans Passant

8
나는 동의한다. 그러나 나는 여전히 using진술을 포기하지 않을 것 입니다. 내가 만든 개체를 처리하기 위해 다른 개체에 의존하는 것은 잘못된 느낌입니다. 이 코드는, 그것의 OK를 들어,하지만 많은 구현 거기 StreamTextWriter거기를 (뿐만 아니라 BCL에). 그것들을 모두 사용하는 코드는 일관성이 있어야합니다.
Jordão

3
예, Jordão에 동의합니다. 프로그래머가 API의 내부 동작을 알고 있기를 원하는 경우 함수 이름을 DoSomethingAndDisposeStream (Stream stream, OtherData data)으로 지정하여 말합니다.
ZZZ 2013

4
@HansPassant XmlDocument.Save()메소드가 Dispose제공된 매개 변수에서 호출 할 문서화 된 위치를 지적 할 수 있습니까 ? 난의 문서에 표시되지 않습니다 Save(XmlWriter)(내가의 FxCop 버그가 발생하고있어 경우), 또는에서 Save()메소드 자체, 또는 문서에 XmlDocument자체.
Ian Boyd

9

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

9

나는 이것을 사용하여 할 것 #pragma warning disable입니다.

.NET Framework 지침에서는 IDisposable.Dispose를 여러 번 호출 할 수있는 방식으로 구현할 것을 권장합니다. 에서 IDisposable.Dispose의 MSDN 설명 :

Dispose 메서드가 여러 번 호출되는 경우 개체는 예외를 throw하지 않아야합니다.

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

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

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


4
CA2202는 코드 분석 경고이며 컴파일러 경고가 아닙니다. #pragma warning disable컴파일러 경고를 억제하는 데만 사용할 수 있습니다. 코드 분석 경고를 표시하지 않으려면 속성을 사용해야합니다.
Martin Liversage 2015 년

2

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

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

동일한 결과를 얻습니다.


1

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

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


1

저는이 문제를 올바른 방법으로 해결하고 싶었습니다. 즉, 경고를 억제하지 않고 모든 일회용 물건을 올바로 폐기하지 않는 것입니다.

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

0

주제에서 벗어 났지만 그룹화에 다른 서식 지정 기술을 사용하는 것이 좋습니다 using.

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으로 중괄호를 생략 할 수 없습니다 .memoryStreamreturn


5
memoryStream.ToArray ()가 범위를 벗어나지 않습니까?
Polyfun

이것은 원래 코드와 절대적으로 동일합니다. ifs를 사용 하여 할 수있는 것처럼 중괄호를 생략했습니다 ( usings 이외 의 다른 기술에 대해서는이 기술을 조언하지는 않겠지 만 ).
Dan Abramov

2
원래 코드에서 memoryStream.ToArray ()는 첫 번째 사용 범위 내에있었습니다. 범위 밖에 있습니다.
Polyfun

정말 감사합니다 return. 방금 당신이 진술 을 의미한다는 것을 깨달았습니다 . 사실입니다. 나는 이것을 반영하기 위해 대답을 편집했습니다.
Dan Abramov

나는 개인적으로 using중괄호가 없으면 코드를 더 취약하게 만든다고 생각합니다 (몇 년 동안의 diff와 병합을 생각해보십시오). joelonsoftware.com/2005/05/11/making-wrong-code-look-wrong & imperialviolet.org/2014/02/22/applebug.html
Tim Abell

0

모든 사용을 피하고 중첩 된 Dispose-Call을 사용하십시오!

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

1
using이 경우 피해야하는 이유를 설명해주세요 .
StuperUser 2010 년

1
using-statement를 중간에 유지할 수 있지만 다른 구문은 해결해야합니다. 논리적 일관되고 모든 방향으로 업그레이드 가능한 솔루션을 얻기 위해 모든 사용을 제거하기로 결정했습니다!
Harry Saltzman 2012 년

0

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, 아니 모든 클래스는 프로그래머의 오용에 대한 방어과 같습니다.

FX Cop은 이것을 알고 경고합니다.

몇 가지 선택이 있습니다.

  • Dispose모든 개체에서 한 번만 호출 하십시오. 사용하지 마십시오using
  • dispose를 두 번 계속 호출하고 코드가 충돌하지 않기를 바랍니다.
  • 경고를 억제하다

-1

스트림을 사용하지 않고 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);
}

이렇게하면 인코딩을 사용하여 문자열에서 byte []로 변환하기 만하면됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.