언어/C#

C# 제너릭 - 3

tsyang 2021. 1. 20. 01:50

검증 가능성과 제약조건


public static bool Min<T>(T o1, T o2)
{
    if (o1.CompareTo(o2) < 0)
        return o1;
    else
        return o2;
}

위와같은 제너릭 메서드는 문제가 있다. T==0 으로 비교하는 과정에서 o1과 o2가 compareTo를 사용할 수 있는 타입인지 알 수 없기 때문이다.

 

따라서 CLR 은 제약조건이라는걸 제공해줘서 타입의 종류를 제한할 수 있게 해준다.

 

public static T Min<T>(T o1, T o2) where T : IComparable<T>
{
    if (o1.CompareTo(o2) < 0)
        return o1;
    else
        return o2;
}

위처럼 where 토큰을 이용하여 T를 IComparable 인터페이스를 구현한 타입으로 제한할 수 있다. 이렇게 되면 IComparable<T>가 CompareTo 메서드를 제공하기때문에 문제 없는 코드가 된다.

  

 

오버로딩

class SomeType<T> { }
class SomeType<T> where T : IComparable<T> {} //불가능
class SomeType<T1, T2> { } //가능

class SomeClass
{
    private static void Method<T>() { }
    private static void Method<T> where T : IComparable<T> () { } //불가능
    private static void Method<T1, T2>() { } //가능
}

위처럼 개수에 의해서 오버로딩은 되지만 제약조건으로 오버로딩은 안 된다. 

 

오버라이드

class A
{
    public virtual void M<T1, T2>() 
        where T1 : struct
        where T2: class
    { }
}

class B
{
    public override void M<T3, T4>()
        where T3 : EventArgs //오류
        where T4 : class //오류
    { }
}

   

위처럼 오버라이드도 안 된다.


 

CLR에서 제공하는 제약사항은 기본 제약조건, 확장 제약조건, 생성자 제약조건이 있다.

 

기본 제약조건

인자의 타입에 대한 제약조건이다. 기본 제약조건은 지정하지 않거나 한 개만 지정할 수 있다. (다중상속 안 되는거랑 같은 이치?) 

 

또한 기본 제약조건으로는 sealed되지 않은 클래스로 참조 타입을 지정할 수 있지만. System의 Array, Delegate, ValueType.. 등과 같이 일부 특별한 참조 타입은 지정할 수 없다.

 

제약조건으로 참조 타입을 지정하면 컴파일러는 타입 인자가 제약 조건에 지정한 타입과 같은 타입이거나 이를 상속한 타입인지 검사한다. 

 

또 특별히 제약조건으로 class와 struct라는 것을 사용할 수 있다. class 제약 조건은 타입 인자로 반드시 참조 타입만 지정해야 함을 뜻한다. 예를 들어 다음과 같은 코드가 가능하다.

 

class SomeClass<T> where T : class
{
    public void Func()
    {
        T temp = null; // T가 참조 타입이므로 문제 없다.
    }
}

 

 

만약 제약조건이 없다면 T가 struct인 경우 null로 할당이 불가하기에 오류가 발생할 것이다.

 

 

확장 제약조건

 

확장 제약조건은 인터페이스 타입으로 제약사항을 지정하기 위해 사용한다. 또 상속처럼 하나 이상의 인터페이스 제약조건을 지정할 수 있다. 

 

인터페이스 외에는 타입 매개변수 제약조건 (or 노출된 타입 제약조건) 이 있는데, 다음의 코드를 보면 된다.

 

class SomeClass
{
    private static List<TBase> ConvertIList<T, TBase> (IList<T> pList)
    where T : TBase //타입 매개변수 제약
    {
        var baseList = new List<TBase>();
        baseList.Add(pList[0]);             // T가 TBase혹은 TBase를 상속한 타입이기에 문제 없다.
        return baseList;
    }
}

 

생성자 제약조건

 

생성자 제약조건은 지정하지 않거나 new() 하나만 지정할 수 있다. 생성자 제약조건을 지정하면 컴파일러는 지정된 타입 인자가 추상 타입이 아니면서 동시에 배개변수가 없는 기본 public 생성자를 정의하고 있는지 검사한다.

 

class SomeClass<T> where T : new()
{
    private static T Func()
    {
        return new T();
    }
}

또한 struct 기본 제약조건과 같이 쓸 수 없는데

class SomeClass<T> where T : struct, new() //불가

왜냐면 이미 struct 타입에는 매개변수 없는 public 생성자가 있기 때문에 new()라는 제약이 필요 없기 때문이다.

 

또한 제약조건 생성자에 매개변수를 지정할 수는 없다. 아마 마이크로소프트의 개발자들이 필요 없다고 생각한듯 하다. (제프리 리처도 동의한다고 한다)

 

 

'언어 > C#' 카테고리의 다른 글

C# - 문자열 - 1  (0) 2021.02.02
C# 인터페이스  (0) 2021.01.27
C# - 제네릭 2  (0) 2021.01.18
C# 제너릭 - 1  (0) 2021.01.09
C# 이벤트  (1) 2021.01.02