본문 바로가기
면접 준비

[면접 준비][C#] 제네릭(Generic)

by 스테디코디스트 2023. 7. 20.
반응형

1. 제네릭(Generic)이란?

- 데이터 형식을 일반화하여 사용이 가능하게 한 형식 매개변수(Type parameter)이다.

- 박싱/언박싱(강제 형번환)이 일어나지 않도록 타입을 미리 지정하는 방식이다.

 

2. 제네릭의 장점

- 지정된 타입과 호환하는 타입에 대해서만 사용할 수 있게하고, 그렇지 않은 경우 컴파일 에러를 발생시켜 타입에 대한 안정성이 높다.

- 런타임 시 데이터 형식이 결정 되는 것이 아닌 코드에서 호출할 때 데이터 형식이 결정되므로 성능 저하가 없다.

- 또한 Object 타입을 이용해 값 타입을 사용한 경우 박싱이나 언박싱이 일어나는데 제네릭을 사용하면 값 타입의 인스턴스 자체를 값 자체로 전달할 수 있어 박싱이 수행되지 않아 성능이 더 좋다.

- 컴파일러가 형변환을 해주기 때문에 개발자가 편리하고, 컴파일 시 타입을 체크하여 에러를 사전에 발견할 수 있다.

- 보통 <T>를 사용하며, 어떤 것을 담아 놓을 컨테이너 역할을 한다.

- 코드의 재사용성을 높일 수 있다.

class Program
{
    static void PrintInt(int i) 
    {
        Console.WriteLine(i); // int형만 출력
    }

    static void PrintString(string s) 
    {
        Console.WriteLine(s); // string형만 출력
    }

    static void PrintGeneric<T>(T g) 
    {
        Console.WriteLine(g); // 받아온 타입에 따라 출력
    }

    static void Main(string[] args)
    {
        PrintInt(111);
        PrintInt("abc"); // -> 오류

        PrintString("abc");
        PrintString(111); // -> 오류

        PrintGeneric<int>(111);
        PrintGeneric<string>("abc");
    }    
}

 

2. 제네릭 클래스

- c#에서는 기본적으로 제공하는 제네릭 클래스가 많다.

- 대표적으로는 List<T>, Dictionary<T>, LinkedList<T>

static void Main(string[] args)
    {
        // List<T> 사용 - 타입을 미리 지정하여 안정성을 높임
        List<int> intList = new List<int>();
        intList.Add(1);
        intList.Add("s"); // -> 에러
    }

 

3. 제네릭 타입 제약

- [where T : 제약조건] 을 제네릭 선언 후 붙여준다.

class Program
{    
    static void PrintValue<T>(T val) where T : struct // 값형으로 제네릭 타입을 제한 
    {
        Console.WriteLine(val);
    }

    static void PrintReference<T>(T refer) where T : class // 참조형으로 제네릭 타입을 제한
    {
        Console.WriteLine(refer);
    }

    static void Main(string[] args)
    {
        PrintValue<int>(111);
        PrintValue<string>("abc"); // -> 오류 : 참조형이기 때문

        PrintReference<int>(111); // -> 오류 : 값형이기 때문
        PrintReference<string>("abc");
    }    
}

 

4. 제네릭 메서드

- 매개변수의 타입을 미리 결정하지 않고 사용시 결정하기 위한 것

class Program
{
    static void Main(string[] args)
    {
        int a = 3;
        int b = 4;
        string c = "c";
        string d = "d";

        SwapInt(ref a, ref b);
        SwapInt(ref c, ref d); // -> 오류 : int형이 아니기 때문

        SwapString(ref a, ref b); // -> 오류 : string형이 아니기 때문
        SwapString(ref c, ref d); 

        // 제네릭 이용 -> 사용하면서 타입이 결정됨
        Swap(ref a, ref b); // int형 swap
        Swap(ref c, ref d); // string형 swap

        Swap(ref a, ref c); // -> 오류 : 두 매개변수의 형이 다르기 때문
    }

    public static void SwapInt(ref int a, ref int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }

    public static void SwapString(ref string a, ref string b)
    {
        string temp = a;
        a = b;
        b = temp;
    }

    // 제네릭 메소드
    public static void Swap<T>(ref T a, ref T b)
    {
        T temp = a;
        a = b;
        b = temp;
    }
}

 


<면접 예시 답안>

제네릭이란 데이터 형식을 일반화하여 사용가능하게 만든 형식 매개변수이며, 형변환시 타입을 미리 지정해 박싱, 언박싱이 일어나지 않도록 하여 안정성과 성능을 높이는 방식입니다.