스트림 객체에 대해 Close () 또는 Dispose ()를 호출해야합니까?


151

같은 클래스 Stream, StreamReader, StreamWriter등을 구현하는 IDisposable인터페이스를 제공합니다. 즉, Dispose()이러한 클래스의 객체에서 메소드를 호출 할 수 있습니다. 또한 public라는 메소드를 정의했습니다 Close(). 이제 객체로 작업을 마치면 무엇을 호출해야하는지에 대해 혼동됩니다. 둘 다 호출하면 어떻게됩니까?

내 현재 코드는 다음과 같습니다

using (Stream responseStream = response.GetResponseStream())
{
   using (StreamReader reader = new StreamReader(responseStream))
   {
      using (StreamWriter writer = new StreamWriter(filename))
      {
         int chunkSize = 1024;
         while (!reader.EndOfStream)
         {
            char[] buffer = new char[chunkSize];
            int count = reader.Read(buffer, 0, chunkSize);
            if (count != 0)
            {
               writer.Write(buffer, 0, count);
            }
         }
         writer.Close();
      }
      reader.Close();
   }
}

보시다시피, 각 객체에서 using()자동으로 Dispose()메소드를 호출하는 구문을 작성했습니다 . 그러나 나는 또한 Close()메소드 를 호출 합니다. 맞아?

스트림 객체를 사용할 때 모범 사례를 제안하십시오. :-)

MSDN 예제는 using()구문을 사용하지 않고 Close()메소드를 호출 합니다.

좋은가요?


ReSharper를 사용하는 경우이를 patter 카탈로그 내에서 "반 패턴"으로 정의 할 수 있습니다. ReSharper는 귀하의 정의와 관련된 각 사용을 오류 / 힌트 / 경고로 표시합니다. ReSharper가 그러한 경우에 QuickFix를 적용하는 방법을 정의 할 수도 있습니다.
Thorsten Hans

3
팁 : 여러 일회용 일회용에 대해 이와 같은 using 문을 사용할 수 있습니다. (StreamWriter writer = new StreamWriter (filename)) {// ... 일부 코드}
Latrova


using 문을 서로 겹쳐 쌓고 하나의 대괄호를 가질 수있는 것과 같이 using 문을 중첩 할 필요는 없습니다. 또 다른 게시물에서, "코드 화살표"를보고 수정하려면 해당 기술을 사용하는 코드 스 니펫에 대한 편집을 제안했습니다 : stackoverflow.com/questions/5282999/…
Timothy Gonzalez

2
@ Suncat2000 여러 using 문을 사용할 수 있지만 중첩 할 수는 없으며 대신 쌓을 수 있습니다. 유형을 제한하는 이와 같은 구문을 의미하지는 않습니다 using (MemoryStream ms1 = new MemoryStream(), ms2 = new MemoryStream()) { }. 나는 당신이 타입을 재정의 할 수있는 이런 식으로 의미합니다 :using (MemoryStream ms = new MemoryStream()) using (FileStream fs = File.OpenRead("c:\\file.txt")) { }
Timothy Gonzalez

답변:


101

Reflector.NET으로 빠르게 점프하면 Close()방법 StreamWriter이 다음과 같습니다.

public override void Close()
{
    this.Dispose(true);
    GC.SuppressFinalize(this);
}

그리고 StreamReader:

public override void Close()
{
    this.Dispose(true);
}

Dispose(bool disposing)에서 무시 StreamReader된다

protected override void Dispose(bool disposing)
{
    try
    {
        if ((this.Closable && disposing) && (this.stream != null))
        {
            this.stream.Close();
        }
    }
    finally
    {
        if (this.Closable && (this.stream != null))
        {
            this.stream = null;
            /* deleted for brevity */
            base.Dispose(disposing);
        }
    }
}

StreamWriter방법은 비슷합니다.

그래서, 코드를 읽는 것은 그것이 분명 당신이 호출 할 수있는 Close()& Dispose()좋아하고 어떤 순서로 당신만큼 자주 스트림에. 어떤 식 으로든 동작을 변경하지 않습니다.

그래서 그것을 사용하는 것이 더 읽을 수 있는지 여부에 내려 오는 Dispose(), Close()및 / 또는 using ( ... ) { ... }.

개인적으로 선호하는 것은 using ( ... ) { ... }가위로 달리지 않는 데 도움이되므로 가능하면 항상 사용하는 것이 좋습니다.

그러나 이것은 정확성에 도움이되지만 가독성은 떨어집니다. C #에는 이미 중괄호를 닫는 것이 너무 많으므로 실제로 어느 것이 스트림에서 클로즈를 수행하는지 어떻게 알 수 있습니까?

그래서 나는 이것을하는 것이 가장 좋다고 생각합니다.

using (var stream = ...)
{
    /* code */

    stream.Close();
}

코드 동작에는 영향을 미치지 않지만 가독성에 도움이됩니다.


20
" C #에는 이미 중괄호를 닫는 것이 너무 많아서 어느 쪽이 실제로 스트림에서 클로즈를 수행하는지 어떻게 알 수 있습니까? "이것이 큰 문제라고 생각하지 않습니다. 스트림이 "적시에"닫힙니다. 즉, 변수가 범위를 벗어나 더 이상 필요하지 않은 경우.
Heinzi

110
흠, 아니, "도대체가 왜 그것을 두 번 닫는 거지?" 읽는 동안 속도 충돌.
Hans Passant

57
중복 Close()통화에 동의하지 않습니다 . 경험이 부족한 사람이 코드를보고 알지 못하는 경우 using1) 코드를 찾아 배우 거나 2) 맹목적 Close()으로 수동 으로 추가하십시오 . 만약 그가 2)를 고르면, 다른 개발자는 중복 된 것을 볼 Close()것입니다. "만두 기"대신 경험이 부족한 개발자에게 지시 하십시오. 나는 경험이없는 개발자에게는 인생을 어렵게 만드는 것이 아니라 경험있는 개발자로 만드는 것을 선호합니다.
R. Martinho Fernandes

14
+ Close ()를 사용하고 / analyze를 켜면 "경고 : CA2202 : Microsoft.Usage : Object 'f'가 'Foo (string)'메소드에 두 번 이상 배치 될 수 있습니다. 시스템 생성을 피하기 위해. ObjectDisposedException 객체에서 Dispose를 두 번 이상 호출하면 안됩니다. : Lines : 41 "따라서 설명서 및 / analyze에 따르면 Close 및 Dispose를 호출하면 현재 구현이 문제가되지 않지만 이후 버전에서는 변경 될 수 있습니다. 그물.
marc40000

4
좋은 답변 +1. 고려해야 할 또 다른 사항. // 닫기와 같은 닫는 괄호 다음에 주석을 추가하거나 왜 초보자 인 것처럼 닫는 괄호 뒤에 명확하지 않은 한 개의 라이너를 추가하십시오. 예를 들어 긴 클래스에서 마지막 닫는 중괄호 뒤에 // End Namespace XXX를 추가하고 두 번째 마지막 닫는 중괄호 뒤에 // End Class YYY를 추가합니다. 이것은 의견이 아닙니다. 그냥 궁금해서 :) 초보자로서, 나는 그런 코드를 보았습니다. 왜 내가 여기 왔는지 강조합니다. 나는 왜 "두 번째 결말이 필요한지"라는 질문을했습니다. 추가 코드 줄이 명확성을 더하지 못한다고 생각합니다. 죄송합니다.
Francis Rodgers

51

아니요, 해당 메소드를 수동으로 호출해서는 안됩니다. using블록 이 끝나면 Dispose()관리되지 않는 리소스를 확보하는 데 도움이 되는 메서드가 자동으로 호출됩니다 (적어도 스트림, 리더 / 라이터 등의 표준 .NET BCL 클래스의 경우). 따라서 다음과 같이 코드를 작성할 수도 있습니다.

using (Stream responseStream = response.GetResponseStream())
    using (StreamReader reader = new StreamReader(responseStream))
        using (StreamWriter writer = new StreamWriter(filename))
        {
            int chunkSize = 1024;
            while (!reader.EndOfStream)
            {
                 char[] buffer = new char[chunkSize];
                 int count = reader.Read(buffer, 0, chunkSize);
                 if (count != 0)
                 {
                     writer.Write(buffer, 0, count);
                 }
            }
         }

Close()메소드는를 호출합니다 Dispose().


1
독자가 처리 할 때 닫혀 있는지 확인하는 래핑되어 있기 때문에 using첫 번째 가 될 필요는 없습니다 . 그럼에도 불구하고 +1responseStreamreader
Isak Savo

The Close method calls Dispose... 라고 말했을 때 혼란 스럽 습니다. 나머지 게시물에서 Dispose()전화 할 것이라고 암시하고 Close()있습니다. 후자를 수동으로 호출해서는 안됩니다. 그들이 서로를 부르고 있다는 말입니까?
Nawaz

@Nawaz, 내 게시물이 혼란 스러웠습니다. Close 메서드는 단순히 Dispose를 호출합니다. 귀하의 경우 관리되지 않는 리소스를 비우려면 처리가 필요합니다. 명령문을 사용하여 코드를 줄임으로써 Dispose 메소드가 호출됩니다.
Darin Dimitrov

3
끔찍한 대답. using블록을 사용할 수 있다고 가정합니다 . 나는 때때로 쓰는 클래스를 구현하고 있으므로 할 수 없습니다.
Jez

5
@Jez 클래스는 IDisposable 인터페이스를 구현해야하고, close가 해당 영역의 표준 용어 인 경우 Close ()도 구현해야 합니다 . 따라서 클래스를 사용하는 클래스 using는 Dispose 패턴을 사용할 수 있습니다 .
Dorus

13

문서에 따르면이 두 가지 방법은 동일합니다.

StreamReader.Close :이 Close 구현은 True 값을 전달하는 Dispose 메서드를 호출합니다.

StreamWriter.Close :이 Close 구현은 True 값을 전달하는 Dispose 메서드를 호출합니다.

Stream.Close :이 메서드는 Dispose를 호출하여 true를 지정하여 모든 리소스를 해제합니다.

따라서 둘 다 동일하게 유효합니다.

/* Option 1, implicitly calling Dispose */
using (StreamWriter writer = new StreamWriter(filename)) { 
   // do something
} 

/* Option 2, explicitly calling Close */
StreamWriter writer = new StreamWriter(filename)
try {
    // do something
}
finally {
    writer.Close();
}

개인적으로, 나는 "노이즈"가 적기 때문에 첫 번째 옵션을 고수했습니다.


5

모두 지원하는 많은 클래스에 Close()Dispose()방법을 두 통화는 상당 할 것이다. 그러나 일부 클래스에서는 닫힌 객체를 다시 열 수 있습니다. 이러한 클래스 중 일부는 재 개설을 허용하기 위해 종료 후 일부 리소스를 유지할 수 있습니다. 다른 것들은 어떤 자원도 살아있게 유지할 수 없지만 명시 적으로 재개를 금지 Close()하도록 플래그를 설정할 수 있습니다 Dispose().

에 대한 계약은 IDisposable.Dispose명시 적으로 다시는 사용되지 않는 객체에서 호출하면 최악의 피해를 입지 않도록 요구하므로 모든 객체에서 IDisposable.Dispose호출되는 메소드 또는 호출하지 않는 메소드를 호출하는 것이 좋습니다 .Dispose()IDisposableClose()


참고로 MSDN 블로그에는 Close and Dispose 재미를 설명하는 기사가 있습니다. blogs.msdn.com/b/kimhamil/archive/2008/03/15/…
Jamie

1

이것은 오래된 질문이지만 이제 각 질문을 차단하지 않고도 문을 사용하여 작성할 수 있습니다. 포함 블록이 완료되면 역순으로 폐기됩니다.

using var responseStream = response.GetResponseStream();
using var reader = new StreamReader(responseStream);
using var writer = new StreamWriter(filename);

int chunkSize = 1024;
while (!reader.EndOfStream)
{
    char[] buffer = new char[chunkSize];
    int count = reader.Read(buffer, 0, chunkSize);
    if (count != 0)
    {
        writer.Write(buffer, 0, count);
    }
}

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/using

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