제네릭이란 무엇일까요?

흔히 제네릭을 형식 매개 변수라고 칭하기도 합니다.

형식 매개 변수?? 매개 변수는 알법도 한데, 형식 매개 변수는 또 뭘까요?


제네릭을 비유하여 설명하자면 '틀' 이라고 말할 수 있겠네요!

한 클래스나 구조체는 사용자가 코딩한 형식으로만 (정의된 형식) 반환 형식이나 매개 변수의 형식이 결정되게 됩니다.

하지만 제네릭 형식을 사용한다면 이건 매우 달라지게 되죠


우선 한 인터페이스를 예로 들어보겠습니다.

interface INonGenericInterface {
    Int32 Add(Int32 num);
    Int32 Sub(Int32 num);
    Int32 Mul(Int32 num);
    Int32 Div(Int32 num);
}


사칙연산을 수행하는 메서드가 정의된 인터페이스입니다.

반환형과 자료형은 Int32 로 정해져있다는 것을 보실 수 있는데요, Int32 형식이 아닌 다른 형식으로 인터페이스를 만들고 싶다면 어떻게 해야할까요?

INonGenericInterface 인터페이스를 또 하나 만들어서 반환형과 자료형을 바꾸기엔 해야할 일이 너무 많겠죠?

특히나 위에서 소개한 인터페이스가 말고 엄청나게 많은 속성과 메서드가 정의된 인터페이스라면 더더욱 힘들게 되겠죠.

이런 경우에 '제네릭' 을 사용하게 됩니다.

.NET 에서 기본으로 제공하는 클래스 중 제네릭을 사용하는 클래스는 'System.Collections.Generic' 에 정의되어 있습니다. (물론 컬렉션만 해당)


그럼 제네릭을 사용한 인터페이스를 보겠습니다.

interface IGenericInterface<T> {
    T Add(T num);
    T Sub(T num);
    T Mul(T num);
    T Div(T num);
}


형식 이름 뒤에 '<T>' 라는게 추가되었네요? T는 형식 매개 변수를 뜻합니다.

얼추 보면 T 라는 틀이 생겼고, 메서드의 반환형과 자료형이 모두 T 로 된 것을 보실 수 있습니다.

이 <T> 라는 형식 매개 변수에는 말 그대로 '형식' 을 매개 변수처럼 넘겨줘야 합니다.

이 말은 이 제네릭 인터페이스를 어떻게 사용해야 하는지를 안내해주죠.


사용은 이렇게 할 수 있습니다.

IGenericInterface<Int32> int32GenericInterface;

형식<자료형> 식별이름;


이렇게 사용한다면 IGenericInterface<T> 라는 제네릭 인터페이스 내부는 'T' 가 모두 'Int32' 형식으로 변하게 됩니다.

그럼 다른 형식을 사용한다면? 다른 형식을 사용해도 결과는 같습니다.


정말 제 설명대로 되는지 확인해보도록 하겠습니다.

우선 테스트에 사용된 코드입니다.

using System;

namespace Test {
    class TestGenericClass<T> {
        public TestGenericClass(T value) {
            Value = value;
        }
        
        public T Value { get; set; }
    }
    class Program {
        public static void Main() {
            
            // Int32, String 그리고 Boolean 형식을 가진 제네릭 클래스 선언
            TestGenericClass<Int32>        intClass    = new TestGenericClass<Int32>(12345);
            TestGenericClass<String>    stringClass    = new TestGenericClass<String>("12345");
            TestGenericClass<Boolean>    boolClass    = new TestGenericClass<Boolean>(true);
            
            // 각 클래스의 이름과 값을 출력해본다.
            Console.WriteLine("클래스의 이름:");
            Console.WriteLine("\tIntClass   : {0}", intClass.GetType());
            Console.WriteLine("\tStringClass: {0}", stringClass.GetType());
            Console.WriteLine("\tBoolClass  : {0}\n", boolClass.GetType());
            
            Console.WriteLine("Value 속성의 반환 값:");
            Console.WriteLine("\tIntClass   : {0} (형식: {1})", intClass.Value, intClass.Value.GetType());
            Console.WriteLine("\tStringClass: {0} (형식: {1})", stringClass.Value, stringClass.Value.GetType());
            Console.WriteLine("\tBoolClass  : {0} (형식: {1})", boolClass.Value, boolClass.Value.GetType());
            
            Console.ReadKey(true);
        }
    }
}


그리고 테스트의 결과 화면입니다.


제네릭 클래스의 이름은 이렇게 정의됩니다.

Namespace.TypeName`CountOfTypeParameter [TypeParam1,TypeParam2, ... ,TypeParamN]


그리고 Value 속성의 반환 값과 값의 형식은 위 코드에서 정의한대로 Int32, String, Boolean 이 맞게 나왔습니다.



결론:

제네릭이란 사용자가 전달하는 "형식" 을 매개 변수로 사용하여 형식 내부에서 형식 매개 변수로 정의된(주로 단일 형식 매개 변수에서는 T를 사용합니다) 형식을 교체하는 것입니다. (비유 하자면 말입니다)




위의 그림처럼 일반 형식은 틀이 고정되어 있어서 한번 만들고 나면 바꿀 수 없지만 제네릭 형식의 경우는 아직 가공되지 않은 형식으로 표현이 가능하겠네요. 가공(형식 매개 변수)을 통해서 유연하게 형식을 표현이 가능해집니다.





긴 글 읽어주셔서 감사합니다.

정말 오랫만의 포스팅입니다. 앞으로 더 열심히 활동하겠습니다! (군인인데 혹한기 끝냈어요!! 야호 ~_~)

+ Recent posts