Constraints on type parameters

wwiki
이동: 둘러보기, 검색

https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/generics/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사이의 상속관계를 적용하려는 경우에 사용한다.

NotNull constraint[편집 | 원본 편집]

Delegate constraints[편집 | 원본 편집]

Enum constraints[편집 | 원본 편집]