프로그래밍 언어 및 IT 정보/C#

c# Generics(제네릭) 이란

Himer_torr 2023. 5. 31. 23:19
반응형

전 회사에서 Pyhton으로만 코딩하다 좋은 기회로 이직하게 되어서 C#을 다시 복습하고자 한다.

물론 기본적으로 전체를 훑는 게 아니라 그동안 잊고 있던 부분들을 다시 복습하기 위해서 글을 남긴다!

여러분도 같이 한번 훑어보면서 공부하는 글이 되길!

 

 

1. C# 제네릭(Generic) 이란?

제네릭(Generic)은 C# 프로그래밍 언어에서 제공하는 강력한 기능으로,

코드의 재사용성유연성을 향상해 주는 도구이다.

제네릭은 데이터 형식을 일반화하여 재사용 가능한 코드를 작성할 수 있게 도와준다.

제네릭을 사용하면 다양한 형식의 데이터를 처리하는 메서드와 클래스를 작성할 수 있으며 컴파일 시점에서 안정성을 보장해 준다!

 

그렇다면 제네릭은 언제 사용되는가?

 1. 여러 데이터 형식에 대해 동일한 로직을 적용해야 할 때

 2. 컬렉션 타입에서 다양한 데이터 형식을 저장하고 관리해야 할 때

 3. 데이터 형식에 따라 다른 연산을 수행해야 할 때

 

 2. 제네릭(Generic) 사용법

이번에는 쉬운 제네릭 메서드를 사용한 예제를 보면서 사용 방법을 알아보자.

       public T GetMaxValue<T>(T a, T b) where T:IComparable<T>
        {
            if (a.CompareTo(b) > 0)
                return a;
            else
                return b;

        }
        
            int maxInt = GetMaxValue<int>(5, 10);
            Console.WriteLine(maxInt);

            string maxString = GetMaxValue<string>("hello", "world");
            Console.WriteLine(maxString);

위 예제를 보면 "GetMaxValue" 라는 제네릭 메서드를 선언하고 있다.

이 메서드는 두 개의 인자 'a'와 'b'를 비교하여 더 큰 값을 반환하는 기능을 가지고 있는데,

제네릭 타입의 매개변수 T는 메서드 호출 시에 실제 데이터 형식으로 대체되며 a, b는 동일한 데이터 형식이어야 한다.

결과

여기서 잠깐 문자열 비교를 했을 때 왜 저렇게 나오는지 궁금할 수 있다.

문자열의 비교는 기본적으로 유니코드 문자 순서에 따라 이루어진다. 유니코드 문자 순서는 문자들을 

비교하여 정렬하기 위해 정의된 표준이며, 각 문자에는 고유한 숫자 코드 포인트가 할당되어 있다.

예를 들어, "hello"와 "world"를 비교하면 "h"와 "w"를 비교하고, 그다음 문자인 "e"와 "o"를 비교하게 된다.

순서상으로 "h"가 "w"보다 앞에 있으므로 "hello"는 "world"보다 작은 값으로 평가된다.

문자열 비교에서는 대소문자를 구분한다. 따라서 "hello"와 "Hello"는 서로 다른 문자열로 간주되어진다.

대소문자를 구분하지 않고 비교하려면 string의 "Compare" 메서드를 사용하거나 "StringComparer" 클래스의

"InvariantCultureIgnoreCase" 등의 비교 옵션을 활용할 수 있다.

 

위에는 제네릭 매서드였다면 아래는 제네릭 클래스에 대한 쉬운 예제이다.

public class Stack<T>
{
    private T[] elements;
    private int top;

    public Stack(int capacity)
    {
        elements = new T[capacity];
        top = -1;
    }

    public void Push(T item)
    {
        if (top == elements.Length - 1)
        {
            Console.WriteLine("Stack is full. Cannot push item.");
            return;
        }

        elements[++top] = item;
    }

    public T Pop()
    {
        if (top == -1)
        {
            Console.WriteLine("Stack is empty. Cannot pop item.");
            return default(T);
        }

        return elements[top--];
    }

    public bool IsEmpty()
    {
        return top == -1;
    }
}

// 예제 사용
Stack<int> intStack = new Stack<int>(5);
intStack.Push(1);
intStack.Push(2);
intStack.Push(3);

Console.WriteLine(intStack.Pop()); // 출력: 3
Console.WriteLine(intStack.Pop()); // 출력: 2
Console.WriteLine(intStack.Pop()); // 출력: 1

Stack<string> stringStack = new Stack<string>(3);
stringStack.Push("Hello");
stringStack.Push("World");

Console.WriteLine(stringStack.Pop()); // 출력: World
Console.WriteLine(stringStack.Pop()); // 출력: Hello

 3. 제네릭(Generic) 클래스들

.net Framework에는 상당히 많은 제네릭 클래스들이 포함되어 있다.

특히 System.Collections.Generic 네임스페이스에 있는 모든 자료구조 관련 클래스들은 제네릭 타입이다.

흔히 쓰는 List<T>, Dictionary<T>, LinkedList<T> 등의 클래스들은 위 네임스페이스 안에 들어있다.

List<string> nameList = new List<string>();
nameList.Add("아이유");
nameList.Add("아이브");

Dictionary<string, int> dic = new Dictionary<string, int>();
dic["빅뱅"] = 100;
dic["방탄소년단"] = 90;

 4. 제네릭(Generic) 제약 조건

제네릭의 제약 조건은 제네릭 타입 매개변수('T')에 적용되는 제한사항을 정의하는 것입니다.

제네릭의 제약 조건을 통해 특정 인터페이스를 구현하도록 강제하거나, 특정 클래스로부터 상속받도록 요구할 수도 있다.

맨 처음 예제 또한 제약조건이 걸려있는 것을 확인할 수 있다.

이 제약 조건을 통해 클래스나 매서드가 특정 동작을 수행하거나 특정 타입에 대한 조작을 할 수 있도록 보장할 수 있다.

public class MyClass<T> where T : 제약조건
{
    // 클래스 내용
}

public void MyMethod<T>(T param) where T : 제약조건
{
    // 메서드 내용
}

제약 조건은 다음과 같은 유형으로 설정할 수 있다.

  1. where T : 클래스명: T는 특정 클래스 또는 해당 클래스를 상속받은 클래스여야 합니다.
  2. where T : 인터페이스명: T는 특정 인터페이스를 구현한 클래스여야 합니다.
  3. where T : new(): T는 매개변수가 없는 기본 생성자를 가져야 합니다.
  4. where T : struct: T는 값 형식(구조체)이어야 합니다.
  5. where T : unmanaged: T는 비관리 형식이어야 합니다. (C# 7.3부터 지원)
  6. where T : enum: T는 열거형이어야 합니다.
  7. where T : delegate: T는 대리자 형식이어야 합니다

5. 제네릭(Generic) 인터페이스

제네릭 인터페이스는 C#에서 제네릭을 활용하여 다양한 형식의 클래스들이 동일한 동작을 수행할 수 있도록

정의하는 인터페이스이다.

제네릭 인터페이스는 아래와 같이 작성할 수 있다.

public interface IRepository<T>
{
    void Add(T item);
    void Remove(T item);
    T GetById(int id);
    IEnumerable<T> GetAll();
}

위 예제는 "IRepository<T>" 라는 제네릭 인터페이스를 정의한 것이다.

이 인터페이스는 "T"라는 제네릭 타입의 매개변수를 가지고 있으며

'Add', 'Remove', 'GetByID', 'GetAll' 등의 메서드를 정의한다.

 

이런 제네릭 인터페이스는 다양한 형식의 클래스들이 공통된 동작을 수행해야 할 때 유용하게 활용된다.

 

 

반응형