[C#] IDisposable

빵욱·2024년 6월 12일

IDisposable ??

IDisposable 인터페이스는 .NET에서 리소스를 명시적으로 해제할 수 있도록 제공하는 메커니즘이라고 한다.

IDisposable 인터페이스를 상속받아 구현하면 된다.

// IDisposable 인터페이스 정의
public interface IDisposable
{
    void Dispose();
}

IDisposable 인터페이스는 Dispose 메서드를 하나 포함하며, 이 메서드는 객체가 사용한 리소스를 해제하는 데 사용된다.
Dispose 메서드를 호출하여 리소스를 해제하면, 가비지 컬렉터가 더 이상 해당 리소스를 관리하지 않는다고 하는데 이는 이렇게 Dispose 메서드를 구현하라는 말로 해석되는 것 같다.

IDisposable 인터페이스를 구현하려면 Dispose 메서드를 정의해야 한다.
보통은 관리되는 리소스와 관리되지 않는 리소스를 구분하여 처리하는 코드를 구현하는 것이 좋다고 한다.

구현

    private IntPtr unmanagedResuorce;

    // private StreamReader managedResource;
    private List<int> numbers;

    private bool disposed = false;

    public MyResource()
    {
        //  리소스 초기화.
        unmanagedResuorce = IntPtr.Zero;
        // managedResource = new StreamReader("Example.txt");
        numbers = new List<int>();

    }

    public void Dispose()
    {
        Dispose(true);
        // GC.SuppressFinalize(this): : 가비지 컬렉터가 Finalize 호출을 건너뛰도록 요청
        // 이미 Dispose 메서드에서 리소스를 해제했으므로 소멸자를 호출할 필요가 없기 때문
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                Console.WriteLine("Managed resource 헤제.");

                // 관리 되는 리로스 해제.
                if(numbers != null)
                {
                    // managedResource.Dispose();
                    // managedResource = null;
                    numbers.Clear();
                    numbers = null;
                }
            }


            Console.WriteLine("Unmanaged resource 헤제.");
            // 관리 되지 않는 리소스 해제
            if (unmanagedResuorce != IntPtr.Zero)
            {
                unmanagedResuorce = IntPtr.Zero;
            }

            disposed = true;
        }
    }

    ~MyResource(   )
    {
        Console.WriteLine("소멸자 실행");
        //  Dispose(false); 는 소멸자에서만 호출 됨.
        Dispose(false);
        
        // 이렇게 구현하는 이유는 GC가 소멸자를 호출할 수도 있기 때문이다.
    }
}

Dispose 메서드

코드를 보면 Dispose 메서드와 Dispose(bool dispoing) 메서드가 있다.

Dispose(bool disposing) 메서드에 관리되는 리소스와 관리 되지 않는 리소스를 파라미터 플래그에 따라 모두 해제하거나 관리되지 않는 리소드만 해제할 수 있도록 코드를 작성했다.

 public void Dispose()
{
    Dispose(true);
    // GC.SuppressFinalize(this): : 가비지 컬렉터가 Finalize 호출을 건너뛰도록 요청
    // 이미 Dispose 메서드에서 리소스를 해제했으므로 소멸자를 호출할 필요가 없기 때문
    GC.SuppressFinalize(this);
}

Dispose() 메서드 안에서 Dispose(bool dispoing)SuppressFinalize를 호출한다.

Dispose() 메서드를 호출하는 경우는 명시적으로 리소스를 사용자(개발자)가 해제한 것이기 때문에 Dispose(true); 를 통해 관리/관리되지 않늠 모든 리소스를 해제했다.
관리되는 리소스까지 모두 직접 해제했기 때문에 GC(가비지컬렉터) 이중으로 관리되는 리소스를 해제하는 것을 막기 위해서다.

소멸자

 ~MyResource(   )
{
    Console.WriteLine("소멸자 실행");
    //  Dispose(false); 는 소멸자에서만 호출 됨.
    Dispose(false);
    
    // 이렇게 구현하는 이유는 GC가 소멸자를 호출할 수도 있기 때문이다.
}

소멸자를 이렇게 구현한 이유는 소멸자가 호출된 것은 GC가 리소스를 해제한 것이기 때문에
관리되는 리소스를 이미 GC가 관리했을 것이다. 그래서 관리되지 않는 리소스만 해제하는
Dispose(false); 를 호출했다.

Dispose(bool disposing) 를 가상 메서드로 작성

Dispose(bool disposing)를 가상 메서드로 작성한 이유는 상속 때문이다.
클래스가 상속될 때 자식 클래스에서도 적절히 리소스를 해제할 수 있도록 하기 위해 가상 메서드로 작성했다.
만약 어떤 클래스가 IDisposable를 구현한 부모 클래스를 상속받았으면
자식클래스의 Dispose(bool disposing) 메서드는 대략 아래와 같이 만들면 될 것이다.

protected override void Dispose(bool disposing)
   {
       if (!disposed)
       {
           if (disposing)
           {
               // 자식 클래스의 관리되는 리소스 해제
               if (managedResource != null)
               {
                   managedResource.Dispose();
                   managedResource = null;
               }
           }

           // 자식 클래스의 관리되지 않는 리소스 해제 (필요시)

           disposed = true;
       }

       // 기본 클래스의 Dispose 호출
       base.Dispose(disposing);
   }

테스트

 using (MyResource test1 = new MyResource())
{

}

콘솔에서 using를 사용해 간단하게 테스트 했다.

profile
rove drink eat

0개의 댓글