박싱과 언박싱에 대한 포스트에서 박싱으로 인한 문제점에 대한 해결책으로 제네릭을 언급했다.
제네릭은 C# 2.0에서 추가된 개념으로 박싱 문제를 해결하고 코드의 중복을 줄여줬다.
제네릭을 사용했을 때의 효과와 사용법에 대해 알아보자.
BCL에서 제공되는 기본 컬렉션 중 하나인 ArrayList와 이에 대응되는 List<T>를 예로 설명하겠다.
ArrayList는 내부적으로 object의 배열을 가지고 있다.
그래서 타입에 상관없이 값을 보관할 수 있는 장점이 있다.
ArrayList al = new ArrayList();
al.Add(1); // int
al.Add("2"); // string
al.Add(3.0f); // float
하지만 이는 값 형식의 데이터에 한해서 반드시 박싱을 일으키게 한다.
ArrayList를 메모리에 표현하면 아래와 같다.
ArrayList는 값 자체를 저장할 수 없다.
값 자체는 object 타입으로 박싱되어 메모리 어딘가에 할당돼있기 때문이다.
따라서 ArrayList는 박싱된 데이터의 주소를 배열에 저장한다.
만약 ArrayList를 박싱 없이 사용하고 싶다면 각 타입별로 따로 정의해야 할 것이다. (IntArray, FloatArray, ...)
이는 중복되는 코드를 늘리는 굉장히 비효율적인 프로그래밍이다.
제네릭이 추가되면서 한 번의 정의로 위의 박싱 문제와 코드 중복을 해결할 수 있게 됐다.
List<T>는 내부적으로 T 타입의 배열을 가지고 있다.
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
값 형식의 List를 선언할 경우 해당 값을 그대로 복사해오기 때문에 박싱이 일어나지 않는다.
비록 ArrayList와는 다르게 타입을 지정해야 되지만 제네릭 타입은 컴파일 타임에 정해지기 때문에 한 번의 정의로 여러 타입에 대해 적용이 가능하다.
List<float> floatList = new List<float>();
List<char> charList = new List<char>():
기본적인 사용법에 대한 정리는 생략하겠다.
제네릭 클래스나 메서드를 선언할 때 <> 내부의 형식 매개변수에 제약 조건을 걸 수 있다.
where 형식매개변수 : 제약조건1, 제약조건2, ...
형식 매개변수가 여러개인 경우 제약 조건은 여러개도 가능하다.
where 형식매개변수1 : 제약조건1, 제약조건2, ... where 형식매개변수2 : 제약조건1, 제약조건2, ...
제약 조건을 어떻게 하느냐에 따라 제네릭의 활용도가 결정된다.
예를 들어 제약 조건을 특정 인터페이스로 설정할 경우 해당 인터페이스를 구현한 타입만 형식 매개변수로 사용할 수 있다.
즉 다형성을 활용할 수 있다.
아래는 C#에서 지원하는 특별한 제약 조건이다.
제약 조건 | 설명 |
---|---|
where T : struct | 값 형식만 허용한다. (struct만 허용하는게 아니다.) |
where T : class | 참조 형식만 허용한다. |
where T : new() | 기본 생성자를 포함한 경우만 허용한다. 이는 내부에서 new T()로 인스턴스를 생성함을 암시하는 의미다. |
where T : U | 또 다른 형식 매개변수 U 자체거나 이를 상속받은 경우만 허용한다. |
where T : Delegate | C# 7.3에 추가되었다. where T : class에서 좀 더 세분화되어 델리게이트만을 허용할 수 있다. |
where T : Enum | C# 7.3에 추가되었다. where T : struct에서 좀 더 세분화되어 열거형만을 허용할 수 있다. |
where T : unmanaged | C# 7.3에 추가되었다. where T : struct가 좀 더 강화되어 필드로 참조 형식을 가질 수 없게 된다. 즉 객체가 연속된 메모리를 갖게 되는 것이므로 T* 포인터 연산이 가능해진다. |
where T : default | C# 9.0에 추가되었다. 제약 조건을 생략할 경우 자동으로 where T : class로 지정하는 업데이트 이후로 발생하는 문제를 해결하기 위해 생겼다는데 그냥 이런게 있다는거만 알고 넘어가자. (솔직히 안쓸거 같다.) |
참고 자료
시작하세요! C# 10 프로그래밍 - 정성태