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(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)를 가상 메서드로 작성한 이유는 상속 때문이다.
클래스가 상속될 때 자식 클래스에서도 적절히 리소스를 해제할 수 있도록 하기 위해 가상 메서드로 작성했다.
만약 어떤 클래스가 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를 사용해 간단하게 테스트 했다.