자료형들을 일반화하여 다양한 자료형에 대해 T하나로 정의할 수 있다.
class MyList<T>
{
T[] arr = new T[10];
public T GetItem(int i)
{
return arr[i];
}
}
인자가 여러개여도 사용이 가능하다.
dictionary처럼 선언하면 된다.
class MyList<T, K> // ,(쉼표)를 이용해 K라는 타입을 추가한 것
// 딕셔너리 선언 예시
Dictionary<int, string> dic = new Dictionary<int, string>();
일반화 클래스에는 where를 추가하여 조건을 추가할 수 있다.
사용방법
class 클래스 이름<T> where T: 제약 조건
// 어떠한 T(type)이든 상관 없지만 단 T는 반드시 Monster형식이거나 Monster를 상속받아야 한다.
class MyList<T> where T: Monster
{
T[] arr = new T[10];
// 리스트에 포함된 아이템을 가져오는 함수
public T GetItem(int i)
{
return arr[i];
}
}
class Monster
{
// Monster클래스
}
| 제약 조건 | 설명 |
|---|---|
| where T : struct | T는 값 형식이어야 한다. |
| where T : class | T는 참조 형식이어야 한다. |
| where T : new() | T는 매개변수가 없는 생성자를 가져야 한다. |
| where T : | T는 특정 인터페이스를 구현해야 한다. |
| where T : U | T는U 형식으로부터 상속받거나 U와 동일한 형식이어야 한다. |
| where T : U, V | T는 U와 V형식으로부터 상속받거나 U와 V와 동일한 형식이어야 한다. |
나만의 리스트 MyList라는 자료구조를 만든다고 생각해보자.
자료형마다 클래스를 만든다
class MyIntList
{
int[] arr = new int[10];
}
class MyShortList
{
short[] arr = new short[10];
}
class MyFloatList
{
float[] arr = new float[10];
}
class MyDoubleList
{
double[] arr = new double[10];
}
...
이렇게 타입마다 클래스를 만드는 것은 어떤 타입을 사용할지 예측하기 힘들고 매번 추가해야 하기 때문에 굉장히 비효율적이다.
object 자료형을 사용한 방법
object 자료형은 모든 자료형을 담을 수 있지만 언박싱 할때 캐스팅이 필요하다.
// int형을 담은 obj1
object obj1 = 3;
// float형을 담은 obj2
object obj2 = 3.14;
// string형을 담은 obj3
object obj = "hello world!";
// obj를 int형인 number에 대입하기 위해 int형으로 캐스팅
int number = (int)obj1;
var 타입을 사용하면 더 편하지 않을까?
결론부터 말하자면 object타입과 var타입은 개념이 완전히 다르다.
var타입은 우리가 타입을 명시적으로 선언해주지 않아도 알아서 맞게 변환해주고,
object타입은 타입 자체가 object인 것이다.
// string str = "Hello world"; 와 똑같은 구문이라고 볼 수 있다.
var str = "Hello World!";
// 선언된 str_obj는 string이 아니라 object로 선언된 객체이다.
object str_obj = "Hello World!";
c#의 기본 자료형은 object를 상속받기 때문에 캐스팅을 이용해 변환이 가능한 것이다.
그럼 모든 타입을
object로 선언하면 편하지 않을까?
object타입에 데이터를 넣고 빼는 속도가 굉장히 느리다. (박싱/언박싱)
흔히 int나 float같은 기본 자료형은(string 제외) 스택에 저장되는 복사 타입의 변수지만 object는 무조건 참조 타입으로 작동한다.
아래같은 상황을 가정해보자.
int num1 = 3 // 스택에 할당
object num2 = 5 // 힙에 할당
num1을 꺼낼 때는 스택에서 바로 불러올 수 있지만 num2를 꺼내게 된다면 힙에 할당된 값을 스택에 저장하고 불러온다.
박싱 - 언박싱 과정

결국 언박싱이 무거운 작업이기 때문에
Object를 이용한 방법도 좋은 방법은 아니다.
// object를 이용한 Mylist
class MyList
{
object[]arr = new object[10];
}
Generic형식을 사용하는 방법
이름<타입>
MyList를 Generic 클래스로 만들어 보자
class MyList<T>
{
T[] arr = new T[10];
}
T는 Type의 약어로 자주 사용된다.
T 자리에는 내가 사용하고 싶은 타입을 넣어서 사용하면 된다.
using System;
class Program
{
// 제너릭 클래스
class MyList<T>
{
T[] arr = new T[10];
}
class AnyType
{
// 사용자가 정의한 타입
}
static void Main(string[] args)
{
// int 자료형으로 만든 MyList
MyList<int> MyIntList = new MyList<int>();
// float 자료형으로 만든 MyList
MyList<float> MyFloatList = new MyList<float>();
// 사용자가 정의한 자료형으로 만든 MyList
MyList<AnyType> MyList = new MyList<AnyType>();
}
}
이렇게 제너릭 형식으로 만든다면 자료형을 마다 전부 선언할 필요 없이 사용할 때
T자리에 원하는 자료형을 사용하면 된다.
인프런 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part1: C# 기초 프로그래밍 입문 강의를 듣고 정리한 글 입니다.
개인 공부를 정리한 글이기 때문에 틀린 부분이 있을 수 있습니다.