방프리

20.07.11 Chapter2. .Net 리소스 관리 (Item 17) 본문

C#/Effective C#

20.07.11 Chapter2. .Net 리소스 관리 (Item 17)

방프리 2020. 7. 11. 21:39

Item 17: 표준 Dispose 패턴을 구현하라

 

C++에서 코딩할 때 가장 중요한 것은 메모리 관리이다. 솔직히 DirectX나 학교 과제로 MFC 공부할 때, 특히 서버 개발

일을 할 때 메모리 관리하는 것은 굉장히 신경쓰이고 짜증나는 일이었다. C#에서는 가비지 컬렉터가 알아서 

처리해주니까 이에 대한 부담감이 확실히 덜하긴 하지만 내부적으로 봤을 때 가비지 컬렉터도 코드로 돌아가고 있는

존재이기 때문에 마냥 안심만은 할 수가 없다. 또한 메모리라는 것이 가비지컬렉터가 알아서 해주면 편하지만 이 시점을

개발자들이 제대로 알 수 없기 때문에 버그를 만들 수도 있다. 예로 언리얼 엔진 서밋에 참가했을 때 맨 처음 C#으로 

개발을 진행했던 프로젝트가 메모리가 해제되는 시점을 제대로 알 수 없어서 C++로 다시 바꾸었다는 이야기를 들은

적이 있다. 그렇다고 C++처럼 소멸자를 사용하려고하면 제대로 동작도 안되고 애초에 MSDN에서도 추천하지 않는

방식이다. 그런 문제 때문에 Dispose라는 함수를 통해 표준화된 메모리 해제에 대한 장치를 마련해놨는데 finalizer와

연계되어 동작한다. 

이 부분을 위해서 최상위 베이스 클래스에 다음을 구현해야 한다.

* 리소스를 정리하기 위해서 IDisposeable 인터페이스를 구현

* 멤버 필드로 비관리 리소스를 포함하는 경우에 한해 방어적으로 동작할 수 있도록 finalizer를 추가

* Dispose와 finalizer(존재하는 경우)는 실제 리소스 정리 작업을 수행하는 다른 가상 메서드에 작업을 위임하도록 작성.

파생 클래스가 고유의 리소스 정리 작업이 필요한 경우 이 가상 메서드를 재정의 할 수 있도록 한다.

 

파생클래스에서는 다음을 구현해야 한다.

* 파생 클래스가 고유의 리소스 정리 작업을 수행해야 한다면 베이스 클래스에서 정의한 가상 메서드를 재정의

* 멤버 필드로 비관리 리소스를 포함하는 경우에만 finalizer를 추가

* 베이스 클래스에서 정의하고 있는 가상 함수를 반드시 재호출

 

C#에서도 IDisposable 인터페이스 덕에 런타임에도 개발자가 적시에 리소스를 정리할 수 있다. 

이 인터페이스에서는 단 하나만의 메서드를 가지고 있다.

이 Dispose() 메서드는 다음 네 가지 작업을 무조건 수행한다.

* 모든 비관리 리소스를 정리

* 모든 관리 리소스 정리

* 객체가 이미 정리되었음을 나태내기 위한 상태 플래그 설정. 앞어 이미 정리된 객체에 대하여 추가로 정리 작업이

요청될 경우 이 플래스를 확인하여 ObjectDisposed 예외를 발생

* finalizer 호출 회피. 이를 위해 GC.SupperessFinalize(this) 호출

 

하지만 이 Dispose도 취약한 부분이 있는데 바로 파생 클래스의 메모리 해제 후 베이스 클래스의 메모리 해제 문제이다.

이의 경우엔 protected로 선언된 virtual void Dispose(bool isDisposing); 이라는 함수를 통해 해결이 가능하다.

이 가상 함수를 통해 fianlizer와 Dispose 양쪽에서 사용이 가능하다. 또한 코드 마지막 부분에서는 베이스 클래스의 

Dispose()를 반드시 구현해주어야 한다.

IDisposable을 사용한 베이스 클래스
IDisposable를 사용한 파생 클래스

Dispose와 finalizer는 방어적으로 코딩되어야 한다. 그 부분 때문에 위의 코드에서도 return 하는 부분이 추가되었다.

베이스 클래스의 ObjectDisposedExceptioin 예외는 이미 정리된 리소스를 다시 정리하려고 시도할 때의 예외처리

를 위한 구문이다. Dispose는 분명 메모리를 해제하는 부분이다. 어떠한 이유든지 이 부분에서 절대 객체에 메모리를

할당하는 우범을 범하지 않도록 해야한다. 

Dispose를 구현한다면 표준을 따르자. 그리고 애초에 소멸자의 구현은 C#에서는 그다지 추천하지 않는다.

Comments