이펙티브 c#
아이템 21: 타입 매개변수가 IDisposable을 지원할 경우를 대비하여 제네릭 클래스를 작성하라
제네릭의 역활
- 런타임 오류가 발생할 가능성이 있는 부분을 컴파일 타임 오류로 대체 가능
- 타입 매개변수로 사용할 수있는 타입을 명확히 규정하여 사용자에게 도움을 준다.
타입 매개변수로 지정하는 타입이 IDisposable을 구현하고 있다면 특별한 추가 작업이 반드시 필요하다.
타입 매개변수로 인스턴스를 생성한 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| namespace EffectiveCSharp.Item21 { public interface IEngine { void DoWork(); }
public class EnginDriverOne<T> where T : IEngine, new() { public void GetThingDone() { T driver = new T(); driver.DoWork(); } } }
|
위에 코드에서 T 타입이 IDisposable을 구현하고 있다면 매모리 누수가 발생할수 있다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| using System;
namespace EffectiveCSharp.Item21 { public interface IEngine { void DoWork(); }
public class EnginDriverOne<T> where T : IEngine, new() { public void GetThingDone() { T driver = new T();
using (driver as IDisposable) { driver.DoWork(); } } } }
|
위처럼 간단한 관용구로 메모리 누수를 해결할수 있다. 타입 매개변수를 사용하여 인스턴스를 생성했다면 using 구문을 반드시 사용하자
타입 매개변수로 멤버 변수를 선언한 경우
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| using System;
namespace EffectiveCSharp.Item21 { public class EnginDriverTwo<T> : IDisposable where T : IEngine, new() { private Lazy<T> driver = new(()=> new T()); public void GetThingDone() => driver.Value.DoWork(); public void Dispose() { if (driver.IsValueCreated) { var resource = driver.Value as IDisposable; resource?.Dispose(); } } } }
|
타입 매개변수로 맴버 변수를 선언하면 IDisposable을 상속 받아서 Dispose를 구현해주면 된다.
sealed 를 활용한 제한
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| namespace EffectiveCSharp.Item21 { public sealed class EnginDriver<T> where T : IEngine { private T driver;
public EnginDriver(T driver) { this.driver = driver; }
public void GetThingDone() { driver.DoWork(); } } }
|
sealed를 사용하면 파생 클래스를 생성할수 없으므로 Dispose를 구현하지 않아도 된다.
참조