전체 코드
using System.ComponentModel;
using System.Numerics;
using System.Threading;
using System.Collections.Generic;
namespace CSharp
{
class MyList<T> where T : struct
{
T[] arr = new T[10];
public T GetItem(int i)
{
return arr[i];
}
}
class Program
{
static void Test<T>(T input)
{
}
static void Main(string[] args)
{
MyList<int> myIntList = new MyList<int>();
int item = myIntList.GetItem(0);
MyList<short> myShortList = new MyList<short>();
MyList<Monster> myMonsterList = new MyList<Monster>();
Test<int>(3);
Test<float>(3.0f);
}
}
}
1️⃣ 왜 제네릭이 필요할까?
✔️ 제네릭 없는 반복 코드 문제
class MyIntList { int[] arr = new int[10]; }
class MyFloatList { float[] arr = new float[10]; }
class MyMonsterList { Monster[] arr = new Monster[10]; }
- 자료형마다 같은 클래스 구조를 반복해서 만들어야 함
- 비효율적, 유지보수 어려움
✔️ object를 활용한 일반화 시도 (비추)
class MyList { object[] arr = new object[10]; }
- 자료형에 관계없이 저장 가능
- 하지만 박싱/언박싱 발생 → 성능 저하
- 타입 안전성 보장 어려움 (잘못된 캐스팅 시 런타임 에러)
✔️ 최적해: 제네릭(Generic) 도입
class MyList<T> { T[] arr = new T[10]; }
- 자료형마다 클래스 반복 작성 필요 없음
- 박싱/언박싱 없이 컴파일 타임에 타입 결정 (성능 우수)
- 타입 안정성 보장 (컴파일 시 타입 체크)
2️⃣ 제네릭 클래스 기본 문법
class MyList<T>
{
T[] arr = new T[10];
public T GetItem(int i) { return arr[i]; }
}
| 요소 | 설명 |
|---|
<T> | 제네릭 타입 매개변수 |
T | 실제 타입 자리 |
arr | T 타입 배열 |
3️⃣ 제네릭 클래스 사용 예
MyList<int> intList = new MyList<int>();
MyList<Monster> monsterList = new MyList<Monster>();
T가 int 또는 Monster로 치환되어 컴파일됨
- 타입별 맞춤 클래스 생성 효과
4️⃣ 제네릭 메서드
static void Test<T>(T input)
{
Console.WriteLine(input);
}
| 요소 | 설명 |
|---|
<T> | 제네릭 타입 매개변수 |
T input | 입력 매개변수 타입 일반화 |
호출 예
Test<int>(3);
Test<string>("Hello");
5️⃣ 박싱/언박싱 문제와 제네릭 비교
✔️ object 사용 시 (비효율)
object obj = 3;
int num = (int)obj;
| 작업 | 설명 |
|---|
| 박싱 | 값 타입을 힙에 감싸 저장 |
| 언박싱 | 힙에서 값 타입 꺼내오기 |
| 성능 | 느림 (힙 메모리 사용) |
✔️ 제네릭 사용 시 (효율적)
MyList<int> list = new MyList<int>();
int item = list.GetItem(0);
| 작업 | 설명 |
|---|
| 타입 지정 | 컴파일 시 타입 결정 |
| 성능 | 빠름 (스택에 직접 저장 가능) |
| 타입 안전성 | 높음 (컴파일 타임 체크) |
6️⃣ 제네릭 제약 조건 (where)
| 제약 조건 | 설명 |
|---|
where T : struct | 값 타입만 가능 (int, float 등) |
where T : class | 참조 타입만 가능 (string, Monster 등) |
where T : new() | 기본 생성자 필요 |
where T : 특정클래스 | 특정 클래스 상속 필요 |
where T : 인터페이스 | 특정 인터페이스 구현 필요 |
예시
class MyList<T> where T : new() { ... }
7️⃣ 두 개 이상의 타입 매개변수
class MyList2<T, N>
{
T[] arr = new T[10];
}
- 필요에 따라 여러 타입 매개변수 사용 가능
- 대표적 예:
Dictionary<TKey, TValue>