[C#] Generic (일반화)

ssungho·2023년 7월 13일
0

💡 CSharp Study

목록 보기
1/7
post-thumbnail

💡 Generic 클래스

자료형들을 일반화하여 다양한 자료형에 대해 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 조건

일반화 클래스에는 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 : structT는 값 형식이어야 한다.
where T : classT는 참조 형식이어야 한다.
where T : new()T는 매개변수가 없는 생성자를 가져야 한다.
where T : T는 특정 인터페이스를 구현해야 한다.
where T : UT는U 형식으로부터 상속받거나 U와 동일한 형식이어야 한다.
where T : U, VT는 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타입에 데이터를 넣고 빼는 속도가 굉장히 느리다. (박싱/언박싱)

흔히 intfloat같은 기본 자료형은(string 제외) 스택에 저장되는 복사 타입의 변수지만 object는 무조건 참조 타입으로 작동한다.

아래같은 상황을 가정해보자.

int num1 = 3 // 스택에 할당
object num2 = 5 // 힙에 할당

num1을 꺼낼 때는 스택에서 바로 불러올 수 있지만 num2를 꺼내게 된다면 힙에 할당된 값을 스택에 저장하고 불러온다.


박싱 - 언박싱 과정


결국 언박싱이 무거운 작업이기 때문에 Object를 이용한 방법도 좋은 방법은 아니다.

// object를 이용한 Mylist
class MyList
{
    object[]arr = new object[10];
} 

📝 Generic을 이용한 방법

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# 기초 프로그래밍 입문 강의를 듣고 정리한 글 입니다.

개인 공부를 정리한 글이기 때문에 틀린 부분이 있을 수 있습니다.

profile
클라이언트 개발자가 되는 그날까지 👊

0개의 댓글