Constraints on type parameters
제약조건은 type argument(형식인수)에서 capabilities(갖추어 있어야 하는 기능)을 컴파일러에 알린다. 제약조건은 where 상황별 키워드를 사용하여 지정한다.
제약조건 | 설명 |
---|---|
where T : struct | null을 허용하지 않는 value type이어야 합니다. |
where T : class | 참조형식이어야 한다. 이 제약조건은 모든 클래스, 인터페이스, delegate, 배열 형식에도 적용된다. C# 8.0이상의 null 허용 컨텍스트에서 T는 null을 허용하지 않아야 한다. |
where T : class? | |
where T : notnull | nullable이 아닌 형식이어야 한다.(띄어쓰지 않음) |
where T : unmanaged | |
where T : new() | 매개변수가 없는 public 생성자가 있어야 한다. 다른 제약조건과 함께 사용하는 경우 마지막에 지정해야 한다. struct 또는 unmanaged 제약조건과 결합할 수 없다. |
where T : <base class name> | base class이거나 파생클래스이어야 한다. c# 8.0이상에서 null 허용 컨텍스트에서 null을 허용하지 않는 참조형식이어야 한다. |
where T : <base class name>? | |
where T : <interface name> | |
where T : <interface name>? | |
where T : U | T는 ???????????? |
목차
Why use constraints[편집 | 원본 편집]
제약조건은 type parameter의 capabilities(기능)과 expectations(기대치)를 지정한다.
public class Employee
{
public Employee(string name, int id) => (Name, ID) = (name, id);
public string Name { get; set; }
public int ID { get; set; }
}
public class GenericList<T> where T : Employee
{
private class Node
{
public Node(T t) => (Next, Data) = (null, t);
public Node Next { get; set; }
public T Data { get; set; }
}
private Node head;
public void AddHead(T t)
{
Node n = new Node(t) { Next = head };
head = n;
}
public IEnumerator<T> GetEnumerator()
{
Node current = head;
while (current != null)
{
yield return current.Data;
current = current.Next;
}
}
public T FindFirstOccurrence(string s)
{
Node current = head;
T t = null;
while (current != null)
{
//The constraint enables access to the Name property.
if (current.Data.Name == s)
{
t = current.Data;
break;
}
else
{
current = current.Next;
}
}
return t;
}
}
제약조건을 통해서 제네릭 클래스에서 Employee.Name 속성을 사용할 수 있다. 여러개의 제약조건을 적용할 수 있으며, 제약조건 자체가 제네릭 형식일 수 있다.
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
// ...
}
== 및 != 연산자는 reference identity만 비교한다. 연산자를 오버로드하는 경우에도 마찬가지다. String 클래스가 == 연산자를 오버로드하지만 출력이 false이다.
public static void OpEqualsTest<T>(T s, T t) where T : class
{
System.Console.WriteLine(s == t);
}
private static void TestStringEquality()
{
string s1 = "target";
System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
string s2 = sb.ToString();
// 이게 false라는 거임
OpEqualsTest<string>(s1, s2);
}
값 일치는 where T : IEqualtable<T> 또는 where T : IComparable<T> 제약조건을 적용해야 한다.
Constraining multiple parameters(여러 매개 변수 제한)[편집 | 원본 편집]
class Base { }
class Test<T, U>
where U : struct
where T : Base, new()
{ }
Unbounded(바인딩되지 않은) type parameters[편집 | 원본 편집]
SampleClass<T>{}처럼 제약조건이 없는 type parameters를 Unbounded type parameters라고 한다. 다음과 같은 규칙이 있다.
- != 및 == 연산자는 사용할 수 없다.
- system.object로/에서 변환하거나 임의의 인터페이스 형식으로 명시적으로 변환할 수 있다.
- null과 비교할 수 있다. type parameter가 value type이면 항상 false를 반환한다.
Type parameters as constraints[편집 | 원본 편집]
public class List<T>
{
public void Add<U>(List<U> items) where U : T {/*...*/}
}
위처럼 멤버함수에서 List를 사용하는 경우 U는 T형식을 취하도록 제약하는 것이 좋다. 제네릭 클래스 정의에서 type parameter를 제약조건으로 사용할 수도 있다.
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }
컴파일러는 type parameter가 system.object에서 파생된다는 점을 제외하고는 아무것도 가정할 수 없기 때문에, 위와 같은 경우는 제한된다. 두 type parameter사이의 상속관계를 적용하려는 경우에 사용한다.