ice rabbit programming

[C#] Generic 본문

Development/C#

[C#] Generic

판교토끼 2020. 5. 24. 00:07

이번 글에서는 아주 중요하고 또 자주 쓰이는 개념인 Generic에 대해서 다루어보려고 한다.

C++의 Template, Java의 Generic과 같은 개념으로 메소드나 클래스를 여러 타입에 대해서 똑같이 쓸 수 있는 것이다.
다음 예시로 필요성을 한 번 보자.

public double SumDouble(double d1, double d2) {
    return d1+d2;
}

public int SumInt(int i1, int i2) {
    return i1+i2;
}

 

만약 Generic을 사용하지 않고 double의 합과 int의 합을 구하고 싶다면 메소드 오버로딩을 통해서 구현했을 것이다. 틀린 구현은 아니지만 작성하기 번거로우며, 같은 동작을 하는 메소드가 두 개 존재하여 가독성을 해치고 혼란을 줄 수 있다. 지금은 두 개 뿐이고, 코드도 간단하지만 만약 계속해서 처리해야할 타입이 늘어나고 로직도 복잡해진다면?

이러한 점을 해결하기 위해 Generic의 개념이 들어간다.

public T Sum<T>(T a, T b) {
    return a+b;
}

public static void Main() {
    var result = Sum<int>(2,3); // 5
}

 

이러면 Main에서 Sum 메소드를 호출했을 때 T에는 int가 들어가게 되는 것이다. C#에 구현되어있는 Collection을 사용할 때 가장 많이 쓰며(자료구조가 여러 타입에 대해 구현되어 있으므로), 클래스와 메소드 모두에 사용할 수 있다.

제너릭은 인자에 타입을 지정해줄 때에도 유용한데, 만약 인자 type을 다음과 같이 object로 지정하며 Generic처럼 사용한다면 불필요한 캐스팅의 남용과 boxing/unboxing 문제가 발생한다.

public object Sum(object a, object b) {
	return a+b;
}

 

하지만 Generic으로 인자 타입을 구성했을 경우에는 boxing/unboxing 문제가 발생하지 않는다. 기본적으로는 object로 할 수 있는 연산만 내부에서 구현이 가능하나, where T : 절을 이용해 다음과 같이 제약을 걸 수 있다.

public static T Max<T>(T a, T b) where T : class, IComparable, new() {
    // class : reference type이어야 함
    // ICaomparable : 비교 가능한 type이어야 함
    // new() : 기본 생성자를 가져야 함
}

 

끝으로 T는 꼭 한 개일 필요는 없고, T1, T2등으로 명명해서 쓸 수 있다. 지난 가변 길이 매개변수 때와 마찬가지로 모든 객체는 object인 점을 이용해 <object>로 제너릭이 모든 타입을 받게 할 수도 있다.

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

[C#] 예외 처리(Exception Handling)  (2) 2021.03.28
[C#] Delegate  (0) 2020.09.05
[C#] Index와 Range  (0) 2020.05.17
[C#] Property와 Indexer  (0) 2020.05.10
[C#] 메소드  (0) 2020.05.04