[유니티/C#] 일반화(Generic)이란? 제네릭 메서드&제네릭 클래스의 선언 방식과 차이 비교, 제네릭 컬렉션 특징 및 활용 사례

728x90

일반화(Generic) 개요 및 제네릭 메서드(Generic Method) & 제네릭 클래스(Generic Class) 선언

 

일반화 제네릭

Generic의 사전적 의미는 '포괄적인', '범용적인', '일반적인'이라는 뜻이다. 어떤  특정한 기법을 타겟팅 하는 것이 아니라 두루두루 사용할 수 있도록 한다.

 

일반적으로 클래스를 정의할 때, 클래스 내의 모든 데이터 타입을 정해준다. 대부분이 동일하지만 일부 데이터 타입만 다른 경우도 존재하는데, 그 예로 int 타입을 파라미터로 받은 메서드는 double 타입의 데이터를 사용할 수 없게 된다.

 

이러한 이슈를 한번에 해결할 수 있는 방법으로 제네릭 타입(Generic Type)을 사용하면 int, float, double 등 특정 데이터 타입을 확정하지 않고 데이터 타입 자체를 타입 파라미터(Type Parameter)로 받아 정의할 수 있다. 대표적으로 유니티에서는 클래스, 인터페이스, 메서드 등에 <T> 타입 파라미터를 붙여 제네릭 타입을 만든다.

 

 

제네릭 선언 방식

일반적인 메서드는 int. double. bool형 등 매개변수를 가진 메서드들을 일일히 생성해줘야 한다. 물론 실행에는 문제 없지만 매우 비효율적인 방식이라고 볼 수 있다. 이를 해결할 수 있는 방법은 제네릭 메서드를 선언해주는 것인데 메서드<T> 형식으로 코드를 작성한다. 호출 코드에서 <>안에 int타입으로 작성하면 T기호는 int 타입으로 치환된다. 다른 타입도 마찬가지이다. 

 

 

제네릭 메서드의 특징

  • 제네릭 메서드는 여러 개의 매개변수를 가질 수 있다.
  • 제네릭 타입을 여러 개 선언할 수 있다.
  • 제네릭 메서드를 호출할 때, 첫 번째 매개변수는 int형을, 두 번째 매개변수는 string형을 동시에 전달할 수 있다.
  • 메서드 반환 타입도 제네릭 형식으로 설정할 수 있다.

 

※Object 타입의 한계

제네릭 메서드 대신 Object 타입으로 매개변수 타입을 설정할 수도 있다. 하지만 정확한 타입의 식별이 어려워 에러 발생시 에러를 찾기 힘들고, 성능 역시 제네릭 타입보다 좋지 않다. 반면, 제네릭 메서드는 호출 시 컴파일러가 타입을 유추하여 제네릭 유형을 적합한 타입으로 치환하게 된다.

 

 

제네릭 클래스

제네릭 클래스는 클래스 내부의 멤버 변수의 데이터 타입을 일반화할 클래스로 특정 타입에 국한되지 않고 모든 타입을 멤버 변수의 타입으로 설정 가능하다.

 

일반 메서드와 유사하게 일반적인 클래스 역시 타입의 갯수만큼 클래스를 구현해야하는 한계를 가지고 있는데 이를 보완하기 위해 제네릭 클래스를 사용해주면 되겠다.

 

※Object 타입 클래스의 한계

Object 타입으로도 모든 Type 설정이 가능하다. 하지만 이는 Decimal 형이라는 명시적 형 변환이 반드시 필요한데, 명시적 캐스팅 과정에서 런타임 문제가 발생할 가능성이 높다. 또한 캐스팅을 하지 않는 경우, int 형으로 자동 변환되지만 유추하여 변환하기 때문에 안전하지 않다.

 

 

제네릭 클래스 선언 방식

<T>는 제네릭 클래스 멤버 변수의 데이터 유형을 표시하는 방법으로 제네릭 클래스 객체를 생성할 때 <>안에 int를 입력하면 멤버 변수는 int로 치환된다. 당연한 이야기지만 int형으로 객체 생성 후 멤버 변수에 문자열을 할당하면 컴파일 에러가 발생하게 된다.

또한 제네릭 클래스의 생성자에서 멤버 변수의 값을 초기화할 수 있다. 멤버 변수 타입에 따라 기본값을 할당해야하는 경우 Default 연산자를 사용하는데 이 경우, 숫자형은 0, 문자형은 " "이 할당된다.

 

 

제네릭 특징 정리

  • 제네릭 메소드(매개변수 타입 일반화) / 제네릭 클래스(멤버변수 타입 일반화)
  • 불필요한 메서드/클래스 구현을 방지한다.
  • 코드의 재사용성이 높고 성능이 좋다
  • 타입 검사가 엄격해서 문제 발생 시 앱 배포 전에 미리 문제를 해결할 수 있다.
  • 명시적 변환보다 속도가 빠르다
  • C#에서는 List<T>, Dictionary<T>, LinkedList<T> 등 기본적으로 제공하는 제네릭 클래스가 있다.

 

제네릭 컬렉션 특징 및 사용 예시

 

제네릭 컬렉션

컬렉션(Collection)데이터의 검색과 저장을 위해 특화된 자료구조로 ArrayList, Hashtable, Queue, Stack 등이 있다. 제네릭 컬렉션(Generic Collection)은 제네릭을 이용한 컬렉션으로 일반 형식의 컬렉션보다 형식 안정성이 보장되고 성능이 높은 컬렉션을 만들 수 있다. 유니티에서는 흔히 using System.Collection.Generic으로 네임스페이스에 추가해서 사용하면 된다.

 

 

컬렉션과 제네릭 컬렉션의 차이

컬렉션은 값을 저장하기 위해 값의 형식을 저장하지 않고도 사용할 수 있으며, 모든 참조나 값 형식은 Object 형식으로 암시적으로 업캐스팅 된다. 또한 항목이 값 형식이면 이를 목록에 추가할 때 boxing을 해야하고 이를 검색할 때 unboxing을 해야 하는데, 캐스팅이나 boxing / unboxing 작업은 모두 성능에 영향을 준다.

 

제네릭 컬렉션은 값을 저장하기 위해 저장하는 값의 형식을 정해야 사용할수 있다. 타입에 맞추어 값을 저장하기 때문에 캐스팅, boxing / unboxing 등의 작업이 발생하지 않는다. 

 

 

유니티에서 제네릭 컬렉션 사용 예시

유니티에서 사용하는 제네릭 컬렉션의 대표적인 사용 예시는 GetComponent<>이다.

이는 Component를 가져오기 위해 제네릭 기법이 적용된 메서드로 모든 타입의 컴포넌트에 대응할 수 있다. 제네릭 기법을 사용하지 않았다면 GetComponentRigidbody() / GetComponentTransform() / GetComponentRenderer() 처럼 컴포넌트를 호출하기 위해 각각 메서드를 선언해줘야 한다.

 

GetComponent<> 안에는 컴포넌트 뿐만 아니라 클래스(스크립트)도 반환이 가능하여 활용성이 매우 높다. 대표적으로 우리가 유니티에서 스크립트를 생성하면 클래스는 기본적으로 MonoBehaviour를 상속받는데, 이를 F12로 타고 올라가보면 MonoBehaviour -> Behaviour -> Component 클래스를 상속받았음을 확인할 수 있다. 따라서 반환 타입은 Component 또는 Component를 상속받은 클래스를 반환했다고 보면 된다. 

 

728x90