본문 바로가기
카테고리 없음

C# 코딩연습 - Generic 프로그래밍

by Mulder5 2020. 11. 22.
반응형

Generic이란 직역해보면 "일반적인"이다. 그렇다면 프로그래밍에서 "일반적인 함수"란 무엇일까? 그 전에 "일반화"란 무엇일까? 네이버 사전에는 "여러 개체들이 가지고 있는 공통된 특성을 부각시켜 한 개념이나 법칙을 성립시키는 과정."라고 되어있다. 그렇다면 이 주제의 프로그래밍 내용에서는 일반화를 서로 다른 것들의 공통점을 찾아내서 함수, 클래스로 만드는 것."이란 개념으로 접근해보면 되겠다. 곧바로 예제를 보면 쉽게 알 수 있다.

// 임의 타입의 단수 매개변수 하나 입력
static void GenericPrint<T>(T data)
{
    // 입력된 값 출력
    Console.WriteLine("data : {0}", data);
}

// 임의 타입의 배열 매개변수 입력
static void GenericPrint<T>(T[] arrData)
{
    if(arrData.Length > 0)
    {
        Console.WriteLine("ArrData length : {0}", arrData.Length);
        // 입력된 배열의 값 모두 출력
        foreach (T data in arrData)
        {
            Console.WriteLine("arrData : {0}", data);
        }
    }
}


static void Main(string[] args)
{
    int a = 10;
    float b = 50.4f;
    string c = "Good Morning";

    int[] arrA = { 0, 1, 2, 3, 4, 5 };
    string[] arrB = { "A", "B", "C", "D", "E" };

    // static void GenericPrint<T>(T data)
    GenericPrint(a);
    GenericPrint(b);
    GenericPrint(c);

    // static void GenericPrint<T>(T[] arrData)
    GenericPrint(arrA);
    GenericPrint(arrB);
}

두개의 동일한 이름의 함수 GenericPrint()가 있다. 이 함수의 표현에서 주목할 점은 함수 이름 바로 뒤에 있는 <T> 이며 이것은 T type으로 매개변수를 일반화 할 것이며, 이 매개변수의 type은 런타임시에 입력 된 값으로 추정, 결정된다. 동일한 이름의 두번째 함수는 배열 입력을 받되,  마찬기지로 배열의 type은 런타임 시에 입력된 값에 따라 결정된다.

이러한 일반화 프로그래밍의 장점은 타입에 따른 함수 정의를 별도로 하지 않아도 되므로 코드의 양을 줄일 수 있겠고, object type을 사용하지 않음으로서 박싱/언박싱의 성능 소모를 줄일 수 있으므로, object type을 사용하기 보다는 일반화 프로그래밍을 사용해야 할 것이다. 

이번에는 클래스를 만들어보자. 이번에도 예제로 바로 직진.

 class GenericAA<T>
 {
     private T num;
     public T GetNum()
     {
         return num;
     }

     public void SetNum(T data)
     {
         num = data;
     }

 }
 class Program
 {
     static void Main(string[] args)
     {
         GenericAA<int> AA = new GenericAA<int>();
         AA.SetNum(100);

         Console.WriteLine("ClassAA value : {0}", AA.GetNum());

         GenericAA<float> BB = new GenericAA<float>();
         BB.SetNum(500.1f);
         Console.WriteLine("Class BB value : {0}", BB.GetNum());


     }
 }

이번에는 클래스 이름 뒤에 일반화를 의미하는 <T>를 붙인다. 이렇게 되면 런타임>생성시에 T의 type이 결정된다. 인스턴스 AA는 int로 사용되고, BB는 float로 사용되었다. 일반화가 없었다면 동일한 형식의 클래스 두 쌍을 만들고 num에 대한 type을 다르게 하여 구현해야 했을 것이다. 이로서 코드량이 반절이 줄어든 샘이다. 참 쉽고 편리한 기능이다!

그렇다면 이렇게 유연한 방식안에서 규칙 또는 제한을 주고 싶다면? 이럴때는 where 키워드를 사용한다.

Class MyList<T> where T : MyClass
{
	//...
}
제약 설명
where T : struct T 형식이어야 한다.
where T : class T 참조 형식이어야 한다.
where T : new() T 반드시 매개 변수가 없는 생성자가 있어야 한다.
where T : 기반클래스이름 T 명시된 클래스의 파생클래스여야 한다.
where T : 인터페이스이름 T 명시된 인터페이스를 반드시 구현해야 한다. 여러 개의 인터페이스가 명시될 있다.
where T : U T 다른 형식 매개변수 U로부터 상속받은 클래스 여야 한다.

 

반응형