C #에서 IDisposable과 소멸자를 사용하는 것의 차이점은 무엇입니까?


101

소멸자가 아닌 클래스에서 IDispose를 언제 구현합니까? 이 기사를 읽었 지만 여전히 요점이 누락되었습니다.

내 가정은 객체에 IDispose를 구현하면 가비지 수집기가 수행하기를 기다리는 대신 명시 적으로 '파괴'할 수 있다는 것입니다. 이 올바른지?

객체에 대해 항상 명시 적으로 Dispose를 호출해야 함을 의미합니까? 이에 대한 일반적인 예는 무엇입니까?


5
실제로 모든 Disposable 개체에 대해 Dispose를 호출해야합니다. using구성을 사용하여 쉽게 할 수 있습니다 .
Luc Touraille

아, 말이 되네요. 나는 항상 왜 'using'문이 파일 스트림에 사용되는지 궁금했습니다. 나는 그것이 객체의 범위와 관련이 있다는 것을 알고 있지만 IDisposable 인터페이스와 관련된 맥락에 넣지 않았습니다.
Jordan Parmer

5
기억해야 할 한 가지 중요한 점 은 종료자가 클래스의 관리되는 멤버에 액세스 해서는 안된다는 것 입니다. 이러한 멤버는 더 이상 유효한 참조가 아닐 수 있기 때문입니다.
Dan Bryant

답변:


126

종료 자 (소멸자라고도 함)는 가비지 수집 (GC)의 일부입니다. GC는 주로 메모리 부족 (즉, 더 많은 공간이 필요함)의 결과로 발생하기 때문에 이것이 발생하는시기 (또는 발생하더라도) 불확실합니다. 종료자는 일반적으로 정리에만 사용됩니다. 관리되지 않는 리소스 관리되는 리소스에는 자체 수집 / 처리가 있기 때문입니다.

그 후 IDisposable 객체 를 결정적으로 정리하는 데 사용됩니다 . 객체의 메모리 (여전히 GC에 속해 있음)를 수집하지 않지만 예를 들어 파일, 데이터베이스 연결 등을 닫는 데 사용됩니다.

이에 대한 이전 주제가 많이 있습니다.

마지막으로, IDisposable객체가 종료 자를 갖는 것은 드문 일이 아닙니다 . 이 경우 Dispose()일반적으로를 호출합니다 GC.SuppressFinalize(this). 즉, GC는 종료자를 실행하지 않고 메모리를 버립니다 (훨씬 저렴). Dispose()객체 를 잊어 버린 경우에도 종료자가 계속 실행됩니다 .


감사! 그것은 완벽하게 이해됩니다. 큰 반응에 감사드립니다.
Jordan Parmer

27
할 말이 하나 더 있습니다. 정말로 필요한 경우가 아니면 종료자를 클래스에 추가하지 마십시오. 종료 자 (소멸자)를 추가하면 GC가이를 호출해야하며 (빈 종료 자 일지라도)이를 호출하려면 객체는 항상 1 세대 가비지 수집에서 살아남습니다. 이것은 GC를 방해하고 느리게합니다. 그것은 Marc가 위 코드에서 SuppressFinalize를 호출하라고 말한 것입니다
Kevin Jones

1
따라서 Finalize는 관리되지 않는 리소스를 해제하는 것입니다. 하지만 Dispose를 사용하여 관리되는 리소스와 관리되지 않는 리소스를 해제 할 수 있습니까?
Dark_Knight

2
@Dark 예; (가) 관리 아래 6 단계 때문에 체인 프롬프트 정리를해야하는 관리되지 않는 하나가 될 수
마크 Gravell

1
@KevinJones 종료자가있는 객체는 1 세대가 아닌 0 세대에서 살아남는 것이 보장됩니다. .NET Performance라는 책에서 읽었습니다.
David Klempfner

25

Finalize()메서드 의 역할은 가비지 수집시 .NET 개체가 관리되지 않는 리소스를 정리할 수 있도록하는 것 입니다. 그러나 데이터베이스 연결 또는 파일 처리기와 같은 개체는 가비지 수집에 의존하는 대신 가능한 한 빨리 해제해야합니다. 이를 위해 IDisposable인터페이스 를 구현 하고 Dispose()메서드 에서 리소스를 해제해야합니다 .


9

MSDN 에 대한 매우 좋은 설명이 있습니다 .

이 인터페이스의 주요 용도는 관리되지 않는 리소스해제하는 것 입니다. 가비지 수집기 는 해당 개체가 더 이상 사용되지 않을 때 관리되는 개체에 할당 된 메모리를 자동으로 해제합니다 . 그러나 가비지 콜렉션이 언제 발생할지 예측할 수 없습니다 . 또한 가비지 수집기 창 핸들이나 열린 파일 및 스트림과 같은 관리되지 않는 리소스에 대한 지식이 없습니다 .

이 인터페이스의 Dispose 메서드를 사용하여 가비지 수집기와 함께 관리되지 않는 리소스 를 명시 적으로 해제 합니다. 개체소비자는 개체가 더 이상 필요하지 않을 때이 메서드를 호출 할 수 있습니다.


1
이 설명의 한 가지 주요 약점은 MS가 관리되지 않는 리소스의 예를 제공하지만 실제로는 용어를 정의한 적이 없다는 것입니다. 관리되는 개체는 일반적으로 관리되는 코드 내에서만 사용할 수 있기 때문에 관리되지 않는 코드에 사용되는 것은 관리되지 않는 리소스라고 생각할 수 있지만 실제로는 그렇지 않습니다. 대부분의 비 관리 코드는 리소스를 사용하지 않으며 이벤트와 같은 일부 관리되지 않는 리소스는 관리되는 코드 유니버스에만 존재합니다.
supercat

1
수명이 짧은 개체가 수명이 긴 개체의 이벤트를 구독하는 경우 (예 : 수명이 짧은 개체의 수명 내에 발생하는 모든 변경 사항에 대한 알림을 받도록 요청) 이러한 이벤트는 관리되지 않는 리소스로 간주되어야합니다. 이벤트를 구독 취소하면 수명이 짧은 개체의 수명이 수명이 긴 개체의 수명으로 연장됩니다. 수천 또는 수백만 개의 단기 객체가 이벤트를 구독했지만 구독을 취소하지 않고 중단 된 경우 메모리 또는 CPU 누수가 발생할 수 있습니다 (각 구독을 처리하는 데 필요한 시간이 증가하기 때문).
supercat

1
관리 코드 내의 관리되지 않는 리소스와 관련된 또 다른 시나리오는 풀에서 개체 할당입니다. 특히 코드를 .NET Micro Framework에서 실행해야하는 경우 (가비지 수집기가 데스크톱 컴퓨터의 것보다 훨씬 덜 효율적 임) 코드가 예를 들어 구조 배열을 갖는 것이 도움이 될 수 있습니다. 각 구조는 "사용됨"으로 표시 될 수 있습니다. 또는 "무료". 할당 요청은 현재 "free"로 표시된 구조를 찾아서 "used"로 표시 한 다음 인덱스를 반환해야합니다. 릴리스 요청은 구조를 "무료"로 표시해야합니다. 할당 요청이 예를 들어 23을 반환하면 ...
supercat

1
... 코드가 더 이상 항목 # 23이 필요하지 않다고 어레이 소유자에게 알리지 않으면 해당 어레이 슬롯은 다른 코드에서 사용할 수 없습니다. GC가 매우 효율적이기 때문에 이러한 배열 슬롯의 수동 할당은 데스크톱 코드에서 자주 사용되지 않지만 Micro Framework에서 실행되는 코드에서는 큰 차이를 만들 수 있습니다.
supercat

8

C # 소멸자에 있어야하는 유일한 것은 다음 줄입니다.

Dispose(False);

그게 다야. 그 방법에는 다른 것이 없어야합니다.


3
이것은 Microsoft가 .NET 문서에서 제안한 디자인 패턴이지만 개체가 IDisposable이 아닌 경우에는 사용하지 마십시오. msdn.microsoft.com/en-us/library/fs2xkftw%28v=vs.110%29.aspx
Zbyl 2013

1
Dispose 메서드가없는 종료자가있는 클래스를 제공해야하는 이유를 생각할 수 없습니다.
Jonathan Allen

4

항상 전화해야하는지 여부에 대한 질문 Dispose은 일반적으로 열띤 논쟁입니다. .NET 커뮤니티에서 존경받는 개인의 흥미로운 관점을 보려면 블로그를 참조하십시오 .

개인적으로 전화 Dispose가 필수가 아니라는 Jeffrey Richter의 입장 은 엄청나게 약하다고 생각합니다. 그는 자신의 의견을 정당화하기 위해 두 가지 예를 제공합니다.

첫 번째 예에서 그는 DisposeWindows Forms 컨트롤을 호출 하는 것이 주류 시나리오에서 지루하고 불필요하다고 말합니다 . 그러나 그는 Dispose이러한 주류 시나리오에서 실제로 제어 컨테이너에 의해 자동으로 호출 된다는 점을 언급하지 않았습니다.

두 번째 예에서 그는 개발자가 IAsyncResult.WaitHandle속성이 대기 핸들을 느리게 초기화하여 불필요한 성능 저하를 초래한다는 사실을 깨닫지 않고 인스턴스 가 공격적으로 처리되어야 한다고 잘못 가정 할 수 있다고 말합니다 . 그러나이 예제의 문제점은 그 IAsyncResult자체가 IDisposable개체 처리에 대한 Microsoft의 자체 게시 지침을 준수하지 않는다는 것 입니다. 즉, 클래스가 IDisposable유형에 대한 참조를 보유하고 있으면 클래스 자체가 IDisposable. IAsyncResult그 규칙을 따를 경우 자체 Dispose방법으로 구성 구성원 중 어느 쪽을 폐기해야하는지 결정할 수 있습니다.

따라서 누군가가 더 설득력있는 주장을하지 않는 한, 저는 대부분 잘못된 디자인 선택으로 인해 발생하는 몇 가지 부가적인 사례가있을 것이라는 이해와 함께 "항상 Dispose를 호출"캠프에 머물 것입니다.


3

정말 간단합니다. 답변을 받았지만 다시 시도하지만 가능한 한 간단하게 유지하려고 노력할 것입니다.

일반적으로 소멸자는 사용하지 않아야합니다. .net이 실행되기를 원합니다. 가비지 수집주기 후에 만 ​​실행됩니다. 애플리케이션의 수명주기 동안에는 실제로 실행되지 않을 수 있습니다. 따라서 '반드시'실행해야하는 소멸자에 코드를 넣지 마십시오. 또한 클래스가 실행될 때 존재하는 기존 개체에 의존 할 수 없습니다 (소멸자가 실행되는 순서가 보장되지 않으므로 이미 정리되었을 수 있음).

정리가 필요한 리소스 (예 : 파일 및 그래픽 핸들)를 생성하는 개체가있을 때마다 IDisposible을 사용해야합니다. 사실, 많은 사람들은 소멸자에 넣은 모든 것이 위에 나열된 이유로 인해 IDisposable해야한다고 주장합니다.

대부분의 클래스는 종료자가 실행될 때 dispose를 호출하지만 이것은 단순히 안전 가드로서 존재하며 절대로 의존해서는 안됩니다. 작업이 끝나면 IDisposable을 구현하는 모든 것을 명시 적으로 폐기해야합니다. IDisposable을 구현하는 경우 종료 자에서 dispose를 호출해야합니다. 예제는 http://msdn.microsoft.com/en-us/library/system.idisposable.aspx 를 참조 하십시오 .


아니요, 가비지 수집기는 Dispose ()를 호출하지 않습니다. 종료 자만 호출합니다.
Marc Gravell

수정했습니다. 클래스는 종료 자에서 dispose를 호출해야하지만 그럴 필요는 없습니다.
DaEagle

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