본문 바로가기
C#/Effective C#

[Effective C#] Item 18 반드시 필요한 제약 조건만 설정하라

by 코모's 2022. 9. 30.
반응형

타입 매개변수에 대한 제약 조건(constranint)은 클래스가 작업을 올바르게 수행하기 위해서 타입 매개변수로 전달할 수 있는 타입의 유형을 제한하는 방법이다.

올바른 작업을 수행하기 위한 제약조건의 수가 늘어나면 개발자가 이를 만족시키기 위해서 수행해야 하는 작업의 양이 늘어나서 이 균형을 유지하는 것이 중요하다.

제약 조건을 설정하지 않으면 런타임에 더 많은 검사를 수행하고 더 자주 형변환을 해야 하고, 리플렉션을 사용해야 할 가능성이 커지낟.

제약 조건을 과도하게 설정하면 해당 클래스를 사용하기 위해 과도하게 추가 작업을 해야한다.

 

제약 조건 왜 사용하나?

 

제약 조건을 설정하면 컴파일러는 System.object에 정의된 pbulic 메서드보다 더 많은 것을 매개변수에 기대할 수 있게된다. 하지만 타입 매개변수로 어떤 타입을 지정할 것인지에 대한 추가 정보가 제공되지 않는다면 컴파일러는 이를 System.Object가 정의하고 있는 최소한의 기능만 제공하는 타입이라고 가정할 수 밖에 없다.

 

제약 조건이 컴파일러에게 좋은 두 가지 측면

 

1. 제네릭 타입을 작성할 때 도움이 된다. 컴파일러는 타입 매개변수로 전달된 타입이 제약 조건으로 설정한 기능을 모두 구현하고 있을 것이라 가정할 수 있다.

2. 컴파일러는 타입 매개변수로 올바른 타입을 지정했는지를 컴파일타임에 확인할 수 있다.

 

제약 조건의 위력

 

제약 조건을 설정하는 대신 형변환이나 런타임 중에 타입 테스트를 수행하도록 코드를 작성할 수 있다. 예를 들어 아래의 코드와 같은 제네릭 메서드는 타입 매개변수 T에 대한 어떠한 제약 조건도 설정하지 않고 IComparable<T>로 형 변환이 가능한지 확인한 후 이 인터페이스가 제공하는 메서드를 수행한다.

public static bool AreEqual<T>(T left, T right) 
{
	if (left == null) 
    	return right == null; 
        
    if (left is IComparable<T>) 
    { 
    	IComparable<T> lval = left as IComparable<T>; 
        
    	if (right is IComparable<T>) 
    		return lval.CompareTo(right) == 0; 
    	else 
    		throw new ArgumentException("Type does not implement IComparable<T>", nameof(right)); 
    }
    else // 실패 
    { 
    	throw new ArgumentException("Type does not implement IComparable<T>", nameof(left)); 
    }
}

위의 코드는 T가 반드시 ICompatable<T>를 구현해야 한다고 제약 조건을 설정하면 동일한 메서드를 더 간단하게 재작성 할 수 있다. 

 

public static bool AreEqual2<T>(T left, T right) where T : IComparable<T> 
	=> left.CompareTo(right) == 0;

제약 조건을 설정하는 위의 코드로 작성하면 런타임에 발생할 오류를 컴파일 타임에 확인할 수 있고 코드도 매우 짧음을 알 수 있다.

 

과도한 제약조건은 독!

 

제약 조건의 위력은 보았지만 이런 제약 조건도 과도하게 설정하면 좋지 않다. 타입 매개변수에 제약 조건을 많이 설정하면 제네릭 타입을 사용하는 것이 큰 부담이 되고 사실상 제네릭 타입을 사용한 의미가 없다. 필요한 제약조건 이라면 설정해야 겠지만 그렇지 않다면 제약 조건의 수를 최소하 하려는 노력은 해야한다.

 

제약 조건 최소화하기 위한 방법

1.제네릭 타입 내에서 반드시 필요한 기능만을 제약 조건으로 설정한다.

2. 차라리 런타임에 특정 인터페이스나 특정 베이스 클래스를상속한 타입인지 확인한 후 사용한다.

반응형