제약 조건은 두 가지 역할을 한다.
1. 런타임 오류가 발생할 가능성이 있는 부분을 컴파일 타임 오류로 대체
2. 타입 매개변수로 사용할 수 있는 타입을 명확히 규정하여 사용자에게 도움
제약 조건은 해당 타입이 특정 인터페이스를 구현하는 등의 특성을 지정할 수 있지만 해당 타입이 특정 작업을 수행하도록 강제하지는 않는다. 예를 들어 IDisposable을 구현하는 타입이라면 특별한 추가 작업이 필요하다고 설명했지만 제약 조건 자체는 해당 타입이 IDisposable을 구현하는지 여부만 확인할 뿐 실제로 IDisposable에서 요구하는 작업을 강제하지는 않는디.
제네릭 메서드 내에서 타입 매개 변수로 주어지는 타입을 이용하여 인스턴스를 생성하는 경우 발생
T가 IDisposable을 구현한 타입을 경우 리소스 누수가 발생할 수 있다. 따라서 T타입으로 지역변수를 생성할 때마다 T가 Disposable을 구현하고 있는지 확인해아 하며 만약 IDisposable을 구현하고 있다면 추가적인 처리를 해야 한다.
public interface IEngine()
{
void DoWork();
}
public class EngineDriverOne<T> where T : IEngine, new()
{
public void GetThingsDone()
{
T driver = new T();
driver.DoWork();
}
}
해결 방법
01 using문 사용
만약 지역변수 driver값이 null인 경우 Dispose()가 호출되지 않고, IDisposable을 구현했다면 using블록을 종료할때 Dispose()메서드가 호출된다.
public void GetThingsDone()
{
T driver = new T();
using(driver as IDisposable)
{
driver.DoWork();
}
}
02 Dispose호출책임을 제네릭 클래스 외부로 전담
객체의 소유권을 제네릭 클래스 외부로 옮긴다면 new() 제약 조건을 제거할 수 있다. 즉 생성된 객체를 외부에서 주입하여 IDisposable을 제거한다.
public sealed class EngineDriver<T> where T : IEngine
{
// null로 초기화
private T driver;
public EngineDriver(T driver)
{
this.driver = driver;
}
public void GetThingsDone()
{
driver.DoWork();
}
}
이렇게 하면 외부에서 생성된 객체의 소유권을 가지게 되어 IDisposable 관리가 용이다.
결론
제네릭 클래스의 타입 매개변수로 객체를 생성하는 경우 이 타입이 IDisposable을 구현하고 있는지 확인해야한다. 항상 방어적으로 코드를 작성하고 객체가 삭제될때 리소스가 누수되지 않도록 주의해야한다.
본 게시글은 Effective C#을 읽고 정리하였습니다.
'책 > Effective C#' 카테고리의 다른 글
Effective C# - Item23 타입 매개변수에 대해 메서드 제약 조건을 설정하려면 델리게이트를 활용하라 (0) | 2023.12.15 |
---|---|
Effective C# - Item 22 공변성과 반공변성을 지원하라 (0) | 2023.12.14 |
Effective C# - Item20 IComparable<T>와 IComparer<T>를 이용하여 객체의 선후 관계를 정의하라 (0) | 2023.12.10 |
Effective C# - Item19 런타임에 타입을 확인하여 최적의 알고리즘을 사용하라 (0) | 2023.12.10 |
Effective C# - Item 18 반드시 필요한 제약 조건만 설정하라 (2) | 2023.12.05 |
댓글