포스트

[C#] 제네릭(Generic)

[C#] 제네릭(Generic)

C#의 제네릭(Generic)

1. 개념 요약

제네릭(Generic) 은 클래스, 구조체, 인터페이스, 메서드 등에 대해 타입을 매개변수로 일반화(generalize) 하여 컴파일 타임에 타입을 지정할 수 있도록 하는 기능이다.

1
2
3
4
public class List<T>
{
    public void Add(T item) { /* ... */ }
}

여기서 T타입 매개변수(Type Parameter) 로, 컴파일 시점에 실제 타입으로 대체된다.


2. 제네릭의 핵심 목적

목적설명
타입 안정성컴파일 시점에 타입 오류를 검출함 (object 기반 컬렉션과 비교)
재사용성다양한 타입에 대해 하나의 코드로 동작하도록 구현 가능
성능 향상박싱/언박싱(특히 값 타입)에 의한 런타임 오버헤드 제거
가독성 및 유지보수반복 코드 제거 → 코드 중복 최소화, 유지보수성 향상

3. 사용 가능한 대상

대상예시
클래스public class Stack<T> {}
구조체public struct Pair<T1, T2> {}
인터페이스interface IRepository<T> {}
메서드public T Max<T>(T a, T b)
델리게이트public delegate void Action<T>()

4. 사용 예시

클래스

1
2
3
4
5
6
public class Repository<T>
{
    private List<T> items = new List<T>();
    public void Add(T item) => items.Add(item);
    public T Get(int index) => items[index];
}

메서드

1
2
3
4
public static T Max<T>(T a, T b) where T : IComparable<T>
{
    return a.CompareTo(b) > 0 ? a : b;
}

5. 제네릭 제약 조건 (Constraints)

주요 제약 키워드

키워드설명
where T : class참조형만 허용
where T : struct값형만 허용
where T : new()매개변수 없는 생성자 필요
where T : BaseClass특정 클래스 또는 인터페이스 상속 필요
where T : interface특정 인터페이스 구현 필요

예시

1
2
3
4
public class Factory<T> where T : new()
{
    public T Create() => new T();
}

6. 런타임 vs 컴파일타임 제네릭

  • C#의 제네릭은 CLR 수준에서 구현된 정적 제네릭
  • 값 타입 T마다 별도 코드 생성됨 (JIT specialization)
  • 참조 타입 T는 공통 IL 코드 공유
  • Java와 달리 C#은 리화 타입(reified types) 지원

7. 제네릭의 응용

제네릭 인터페이스 기반 DI 설계

1
2
3
4
5
public interface IRepository<T>
{
    void Add(T item);
    T GetById(int id);
}

제네릭 메서드와 타입 추론

1
2
3
4
5
6
public T Echo<T>(T value)
{
    return value;
}

var result = Echo("hello"); // 컴파일러가 T를 string으로 추론

제네릭 Delegate/Action/Func

1
2
Func<int, string> intToString = x => x.ToString();
Action<MyType> print = t => Console.WriteLine(t);

8. 주의점

주의 사항설명
런타임 타입 정보가 제한적typeof(T)는 가능하지만, T의 멤버에 접근하려면 리플렉션 필요
제네릭 타입 파라미터는 static 멤버 공유 불가static 필드는 타입별로 개별 생성됨
박싱/언박싱 줄일 수 있지만 참조 타입엔 영향 없음List도 내부적으로는 참조 배열

9. 결론 요약

  • C#의 제네릭은 컴파일 타임 타입 안전성, 재사용성, 성능 최적화를 위한 핵심 기능
  • T, T1, T2, TKey, TValue 등의 타입 파라미터를 통해 범용 코드를 정의할 수 있음
  • 제약 조건(where)을 통해 타입에 대한 요구사항을 명시할 수 있음
  • 실무에서는 컬렉션, DI 패턴, 유틸리티 메서드, 서비스 추상화 등 다양한 곳에 활용됨
  • C#의 제네릭은 리화 타입 기반으로 값 타입에서도 박싱 없이 최적화됨
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.