본문 바로가기
C#

[C#] Dispose 패턴

by 소리쿤 2023. 3. 23.
public class ResourceBase : IDisposable
{
	private bool disposedValue;

	protected virtual void Dispose(bool disposing)
	{
		if (!disposedValue)
		{
			if (disposing)
			{
				// TODO: 관리형 상태(관리형 개체)를 삭제합니다.
			}

			// TODO: 비관리형 리소스(비관리형 개체)를 해제하고 종료자를 재정의합니다.
			// TODO: 큰 필드를 null로 설정합니다.
			disposedValue = true;
		}
	}

	~ResourceBase()
	{
		Dispose(disposing: false);
	}

	public void Dispose()
	{
		Dispose(disposing: true);
        
        	// this에 대한 Finalizer 호출하지 않도록 차단
		GC.SuppressFinalize(this);
	}
}


public class Resource : ResourceBase
{
	private List<Some> somes = new List<Some>();
	private bool disposedValue;

	public Resource()
	{
		for (int i = 0; i < 100000; i++)
		{
			somes.Add(new Some());
		}
	}

	public void DoSomething()
	{
		somes.ForEach(some => some.Do());
	}

	protected override void Dispose(bool disposing)
	{
		if(!disposedValue)
		{
			if (disposing)
			{
				// TODO: 관리형 상태(관리형 개체)를 삭제합니다.
			}

			// TODO: 비관리형 리소스(비관리형 개체)를 해제하고 종료자를 재정의합니다.
			// TODO: 큰 필드를 null로 설정합니다.
			somes.ForEach(some => some.Clear());
			somes.Clear();
			somes = null;

			base.Dispose(disposing);
		}

		disposedValue = true;
	}
}

public class Some
{
	private List<string> bigList = new List<string>();

	public void Do()
	{
		for(int i = 0; i < 100000; i++)
		{
			bigList.Add(i.ToString());
		}
	}

	public void Clear()
	{
		bigList.Clear();
	}
}

class Program
{
	private static Resource res = new Resource();

	static void Main(string[] args)
	{
		res.DoSomething();
		res.Dispose();
	}
}


Disposable 패턴은

 

1. GC에 의해 객체가 수집될 때, .NET이 관리해주지 않는 비관리형 리소스를 어떻게 해제할 것인가?

2. GC에 의해 객체가 언제 수집될 지 모르는데, 거대한 리소스를 가진 객체를 어떻게 효율적으로 제거할 것인가?

 

이 두 질문에서 출발하는 것으로 보임

 

1의 해결법은 "Dispose() 함수 내에 비관리형 리소스를 직접 해제하는 구문을 넣고 호출해라." 이고,

2의 해결법은 "Dispose() 함수 내에 관리형 리소스도 직접 해제하는 구문을 넣고 직접 호출해라." 이다.

 

그래서 IDisposable을 상속받은 Dispose() 에는 비관리, 관리 둘다 해제하는 구문이 필요하다.

 

근데 위에 굳이 둘을 구태여 if문으로 구분하여 해제하는 이유는,

1. 사용자의 실수로 Dispose를 호출하지 않는다면 어떻게 될까?

 

에서 추측할 수 있다.  > 망

 

소멸자에서라도 호출해야 하기 때문이다.

 

위 코드에서 알 수 있듯, 소멸자에는 disposing 인자를 false로 넣어 Dispose(false)를 호출한다.

(관리되는 리소스는 소멸자 호출 시점에 GC에 의해 제거될 것이므로 둘의 구분이 필요하다.)

 

그럼 virtual Dispose문을 하나 더 만드는 이유는?

 

IDisposable 상속 구현 편하라고

 

 

 

 

'C#' 카테고리의 다른 글

[C#] SpinLock 구현  (0) 2023.03.29
[C#] IComparable과 IComparer  (0) 2023.03.26
[C#] Interlocked  (0) 2023.03.20
[C#] 메모리 베리어 예제  (0) 2023.03.06
[C#] 캐시 Special Locality 테스트  (0) 2023.03.05