타입 매개변수가 IDisposable을 구현한 경우를 대비하여 제네릭 클래스를 작성하라
제약조건은 2가지 역할을 한다.
1. 제약조건을 정의함으로써 런타임에서의 오류를 컴파일 타임에서의 오류로 대체할 수 있다.
2. 타입 매개변수로 사용할 수 있는 타입을 명확히 규정해준다.
위의 2가지 역할은 사용자가 해야 할 행동을 제시해준다. 하지만 하지 말아야 하는 행동은 제시하지 않는다.
예를 들어 IDisposable 인터페이스를 가지고 있는 타입을 제네릭 타입에 사용할 경우이다. 만약 우리가 작성한 제네릭 클래스에 IDisposable 인터페이스에 대해 구현이 안되어있다면 메모리 릭이 생길 수 도 있다.
public interface IEngine
{
void Dowork();
}
public class EngineDriverOne<T> where T : IEngine, new()
{
public void GetThingsDone()
{
T driver = new T();
driver.Dowork();
//만약 T가 IDisposable 인터페이스를 가지고 있다면...?
}
}
간단한 예시 코드를 보자 EngineDriverOne은 IEngine 인터페이를 가지고 있는 객체에 대해서 내부적으로 생성하여 테스트를 시작해준다. 자 저기서 T가 IDisposeable 인터페이스를 가지고 있다면..??
해결방안 1. using, as 관용구를 이용한 방법
T driver = new T();
using(driver as IDisposable)
{
driver.Dowork();
}
using, as 관용구를 이용해 IDisposable이 구현되어있다면 Dispose를 호출, 안되어있다면 null로 Dispose를 호출하지 않는다.
해결방안 2. 타입 매개변수를 멤버변수로 직접 생성하여 내부적으로 처리하는 방법
public sealed class EngineDriver<T> : IDisposable where T : IEngine, new()
{
//driver라는 멤버변수는 사용하기 직전까지 생성하지 않는다.
private Lazy<T> driver = new Lazy<T>(()=> new T());
//사용자가 해당 클래스의 GetThingsDone()을 호출하면 driver 멤버변수를 생성하여 작업을 시작한다.
public void GetThingsDone() => driver.Value.Dowork();
//IDisposable 패턴
public void Dispose()
{
//만약 driver를 한번이라도 사용했다면 호출 된다.
if(driver.IsValueCreated)
{
var resource = driver.value as IDisposable;
resource?.Dispose();
}
}
}
seald 예약어로 EngineDriver를 super class로의 사용을 방지했다. 이유는 EngineDriver의 IDisposable유무를 모르는 개발자가 사용하여 파생 클래스의 Dispose를 호출하지 않을 경우를 방지한 것이다. 상속을 못한다는 단점이 있지만 추후에 생길 수 있는 문제를 방지한 부분이다.
해결방안 3. 타입 매개변수를 멤버변수로 가지고 있지만 참조 형태로만 가지고 있는 방법.
public sealed class EngineDriver<T> where T : IEngine
{
private T driver;
public EngineDriver(T driver)
{
this.driver = driver;
}
public void GetThinsDone()
{
driver.Dowork();
}
}
EngineDriver의 멤버변수를 참조 형태로만 가지고 있다가 외부 개발자의 Dispose에 의존하는 방법이다.
위의 방법은 외부에서 생성된 인스턴스를 가져오기 때문에 new() 한정자도 필요 없다.
이렇게 제네릭 클래스에서 Dispose의 처리까지 살펴봤다.
Dispose 패턴에 대한 처리 방법은 다양하고 명확한 정답은 없다. 설계하기 나름이다.
결론
해결방안 1. using, as 관용구를 이용한 방법
해결방안 2. 타입 매개변수를 멤버 변수로 직접 생성하여 Dispose패턴을 직접 구현하는 방법.
해결방안 3. 타입 매개변수를 멤버변수로 가지고 있지만 참조 형태로만 가지고 있는 방법.
'Program > Effective C#' 카테고리의 다른 글
Effective C# ITEM20 (0) | 2020.11.25 |
---|---|
Effective C# ITEM 19 (0) | 2020.11.18 |
Effective C# ITEM 18 (1) | 2020.11.15 |
Effective C# ITEM 17 (0) | 2020.11.08 |
Effective C# ITEM16 (0) | 2020.11.05 |