들어가며
클래스에 제네릭을 적용할 때 제네릭 인자를 특정 클래스를 상속 받은 것만 사용 할 수 있도록 하고 싶은데 어떻게 해야 할지 몰라 찾아 보니 [여기]에 설명이 되어 있길래 번역해본다.
C#은 제네릭 타입을 사용 할 때 제약 사항을 줄 수 있다. 이 제약 사항들을 이용하여 제네릭 타입의 인스턴스를 생성하는 코드를 컴파일 타임에 에러를 찾아 낼 수 있다.
where 키워드를 이용해 제네릭 타입에 대해 하나 또는 이상의 제약사항을 설정 할 수 있다.
GenericTypeName<T> where T : constraint1, constraint2
아래 예제는 보여준다. 제약 사항과 함께하는 제네릭 클래스에 제약사항을
class DataStore<T> where T : class
{
public T Data { get; set; }
}
위에서, 우리는 클래스 제약사항을 적용했다. 이것은 참조된 타입만이 DataStore 클래스를 생성 시 제네릭 인자로 넘겨질수 있다는 것이다. 그래서 당신은 클래스, 인터페이스, 델리게이트 또는 배열 타입을 넘겨 줄 수 있다. 값 타입을 넘기는 것은 컴파일 에러를 발생 시킨다. 그래서 우리는 프리미티브 타입이나 struct 타입을 제네릭 인자로 사용 할 수는 없다.
DataStore<string> store = new DataStore<string>(); // valid
DataStore<MyClass> store = new DataStore<MyClass>(); // valid
DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // valid
DataStore<IEnumerable> store = new DataStore<IMyInterface>(); // valid
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
//DataStore<int> store = new DataStore<int>(); // compile-time error
아래 테이블은 제네릭 타입의 제약 사항을 나열해 놓았다.
제약사항 | 설명 |
class | 타입 인자는 반드시 class, interface, delegate 또는 배열 타입이어야한다 |
class? | 타입 인자는 반드시 nullable 또는 non-nullable class, interface, delegate 또는 배열 타입이어야 한다. |
struct | 타입 인자는 반드시 non-nullable 값 타입이어야 한다. 예를 들어 int, char, bool, float와 같은기본 타입 |
new() | 타입 인자는 반드시 public 인자 없는 생성자를 가지고 있는 레퍼런스 타입이어야 한다. 이 제약 조건은 struct나 unmanaged 제약 조건과 같이 사용 할 수 없다. |
notnull | C# 8.0 부터 사용 가능. 타입 인자는 non-nullable 참조 타입 또는 값 타입을 사용 할 수 있다. 만일 그렇지 않다면 컴파일러는 에러 대신 경고를 발생 시킨다. |
unmanaged | 타입 인자는 반드시 non-nullable unmanaged 타입이어야 한다. |
base class | 타입 인자는 반드시 지정된 클래스를 상속 받아야 한다. Object, Array, ValueType 클래스들은 베이스 클래스로 사용이 불가능 하다. Enum, Delegate, MulticastDelegate는 C# 7.3 이전 버전에서는 사용 불가하다. |
<base class name>? | 타입 인자는 반드시 지정된 nullable 또는 non-nullable 클래스 거나, 이 클래스를 상속 받은 클래스여야 한다. |
<interface name> | 타입 인자는 반드시 지정된 interface를 구현해야 한다. |
<interface name>? | 타입 인자는 반드시 지정된 interface를 구현해야 한다. 이것은 nullable 참조 타입 또는 non-nullable 참조 타입, 값 타입이 될 수 있다. |
where T : U | 타입 인자 T는 반드시 U를 상속 받거나 U여야 한다. |
※ nullable? - https://kukuta.tistory.com/359
where T : struct
아래 예제는 non-nullable 값 타입인 struct 제약 사항에 대해 설명한다.
class DataStore<T> where T : struct
{
public T Data { get; set; }
}
DataStore<int> store = new DataStore<int>(); // valid
DataStore<char> store = new DataStore<char>(); // valid
DataStore<MyStruct> store = new DataStore<MyStruct>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
//DataStore<ArrayList> store = new DataStore<ArrayList>(); // compile-time error
where T : new()
아래 예제는 인자 없는 public 생성자를 가진 클래스 제약 사항에 대해 설명한다.
class DataStore<T> where T : class, new()
{
public T Data { get; set; }
}
DataStore<MyClass> store = new DataStore<MyClass>(); // valid
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<int> store = new DataStore<int>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
where T : baseclass
아래 예제는 base class 제약 사항에 대해 설명한다. base class는 지정된 class, abstract class 또는 인터페이스이거나 이를 상속 받은 클래스어야한다.
class DataStore<T> where T : IEnumerable
{
public T Data { get; set; }
}
DataStore<ArrayList> store = new DataStore<ArrayList>(); // valid
DataStore<List> store = new DataStore<List>(); // valid
//DataStore<string> store = new DataStore<string>(); // compile-time error
//DataStore<int> store = new DataStore<int>(); // compile-time error
//DataStore<IMyInterface> store = new DataStore<IMyInterface>(); // compile-time error
//DataStore<MyClass> store = new DataStore<MyClass>(); // compile-time error