본문 바로가기
책/Effective C#

Effective C# - Item 17 표준 Dispose패턴을 구현하라

by 코딩하는 돼징 2023. 12. 3.
반응형

.NET에서는 비관리 리소스를 System.Runtime.Interop.SafeHandel을 상송한 파생 클래스를 통해 표현하는데 이 클래스 또한 Dispose패턴을 완변하게 구현하고 있다.

 

파생 클래스는 다음 작업을 수행해야 한다.

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

- 멤버 필드로 비관리 리소스를 포함하는 경우에만 finalizer추가해야 한다.

- 베이스 클래스에서 정의하고 있는 가상 함수를 반드시 재호출 해야 한다.

 

비관리 리소스를 포함하고 있다면 무조건 finalizer를 구현해야 한다.


IDisposable인터페이스는 단 하나의 메서드만을 가진다.

public interface IDisposable
{
    void Dispose();
}

 

IDisposable.Dispose() 메서드는 다음 네 가지 작업을 반드시 수행해야한다.

1. 모든 비관리 리소스를 정리한다.

2. 모든 관리 리소스를 정리한다.

3. 객체가 이미 정리되었음을 나타내기 위한 상태 플래그 설정. 앞서 이미 정리된 객체에 대하여 추가로 정리 작업이 요청될 경우 이 플래그를 확인하여 ObjectDisposed 예외를 발생시킨다.

4. finalizer 호출 회피. 이를 위해 GC.SuppressFinalize(this)를 호출한다.

 

IDisposable을 이용함으로써 finalize과정으로 인해 발생하는 불필요한 비용을 피할 수 있다.


Dispose(bool)메서드

protected virtual void Dispose(bool isDisposing)

이 가상함수를 구현해두면 finalizer와 Dispose 양쪽에서 사용할 수 있다. 코드의 마지막 부분에서는 반드시 베이스 클래스에서 정의하고 있는 Dispose(bool)함수를 호출해야 한다. 관리 리소스와 비관리 리소스 모두를 제거하려면 isDisposing으로 true를 전달하고 비관리 리소스만 정리하려면 false를 전달해야 한다. 

 

코드 예시

public class MyResource : IDisposable
{
    private bool disposed = false; // 중복 호출 방지를 위한 플래그

    // 관리 리소스 및 비관리 리소스 관련 코드

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // 관리 리소스 정리
            }

            // 비관리 리소스 정리

            disposed = true;
        }
    }

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

    ~MyResource()
    {
        Dispose(false);
    }
}

 

파생 클래스의 구현 방법에 대해서도 알아보자. 파생클래스가 추가적인 정리 작업을 수행해야 하는 경우 다음과 같이 Dispose(bool)메서드를 재정의 해야 한다.

public class MyDerivedClass : MyBaseClass
{
    private bool disposedDerived = false; // 중복 호출 방지를 위한 플래그

    // 파생 클래스에서의 추가적인 리소스 관련 코드

    protected override void Dispose(bool disposing)
    {
        if (!disposedDerived)
        {
            if (disposing)
            {
                // 파생 클래스에서의 추가적인 관리 리소스 정리
            }

            // 파생 클래스에서의 추가적인 비관리 리소스 정리

            disposedDerived = true;
        }

        // 기본 클래스에서의 Dispose(bool) 메서드 호출
        base.Dispose(disposing);
    }
}

 


Dispose메서드나 finailizer에서는 리소스 정리 작업만을 수행해야 한다. 만약 다른 작업을 수행하는 경우 객체에게 문제를 일으킬 수 있다.

Dispose메서드는 리소스를 정리하는 용도로만 사용된다. 객체의 생성지점과 소멸시점은 프로그램의 정확한 동작에 중요한 역할을 한다. 만약 다른 작업을 하는 경우 원하지 않는 방향으로 갈 수 있다.

또한 Dispose메서드와 finailizer는 각각의 역할이 명확하게 분리되어야한다. 리소스 정리이외에 상태변경과 같은 작업을 수행하지 않는게 좋다.


결론

관리 환경에서는 타입을 정의할 때 finalizer를 반드시 작성해야하는 것은 아니다. 새로 작성할 타입이 비관리 리소르를 포함하거나 혹은 IDisposable을 구현한 다른 타입을 포함해야 하는 경우에만 finalizer를 제한적으로 구현하면 된다.

 

 

 

 

본 게시글은 Effective C#을 읽고 정리하였습니다.

 

 

반응형

댓글