[C#]제네릭(Generic)

2022. 4. 14. 18:47Programming language/C#

    목차

서론

 이전에 쓴 글 중 [C#] Boxing/Unboxing (박싱/언박싱)에 관한것이 있었습니다. 이때 한 이야기 중, 타입이 명시적이다라는 것이 있었는데요. 오늘은 이와 연관이 깊은 제네릭(Generic)에 관해 써보려 합니다.


제네릭(Generic)

 제네릭(Generic)의 사전적 의미는 '포괄적인, 통칭(총칭)의'라는 뜻입니다. 이 말 고도 '일반적인'이라는 뜻 도 있습니다. 프로그래밍적으로 보자면, '아직 정해지지 않았지만, 어쨌든 타입을 총칭'한다라는 뜻이 아닐까 싶습니다.

 

예를 들어 보겠습니다. 아래는 싱글톤객체를 instance로 생성하는 일반적인 로직입니다.

    public class CustomSingleton : MonoBehaviour
    {
        private static object _syncobj = new object();

        private static CustomSingleton _instance = null;
        public static CustomSingleton Instance{
            get {
                if (_instance == null)
                {
                    lock (_syncobj) {
                        if (_instance == null)
                        {
                            GameObject app = new GameObject(typeof(CustomSingleton).ToString());
                            _instance = app.AddComponent<CustomSingleton>();
                            DontDestroyOnLoad(app);
                        }
                    }
                    return _instance; 
                }

                return _instance;
            }
        }
    }

프로젝트에서 몇가지 싱글톤 객체를 사용하는데, 매번 위와 같은 코드를 반복해서 생성해줄까요? 기능도 비슷하고, 다른 것이라고는 오직 '타입'뿐 입니다. 싱글톤 객체로 만들어줄 객체의 타입 말이죠. 여기서 한 가지 방법을 생각해 볼 수 있습니다. 생성할 로직 부분을 상속받게 하는 법입니다.

 

 public class CustomSingleton02 : CustomSingleton
 {

 }
    
 public class CustomSingleton03 : CustomSingleton
 {

 }

음... 해보니, 뭔가 이상합니다. 상속을 받았다지만 Singleton의 타입이 되는 것은 오로지 CustomSingleton입니다. CustomSingleton02, 03의 싱글톤이 아닌 것이지요. 만약 제대로 Singleton의 특성과 로직을 이용하고 싶다면 어떻게 해야 할까요?

 

싱글톤을 생성하는 로직을 작성하고, 싱글톤이 될 타입의 결정을 제네릭으로 두는 것입니다. 타입을 포괄하여 결정을 미룬다고 생각하시면 됩니다. 아래는 바뀐 코드입니다.

    public class CustomSingleton<T> : MonoBehaviour where T : MonoBehaviour
    {
        private static object _syncobj = new object();

        private static T _instance = null;
        public static T Instance{
            get {
                if (_instance == null)
                {
                    lock (_syncobj) {
                        if (_instance == null)
                        {
                            GameObject app = new GameObject(typeof(T).ToString());
                            _instance = app.AddComponent<T>();
                            DontDestroyOnLoad(app);
                        }
                    }
                    return _instance; 
                }

                return _instance;
            }
        }
    }

'T'라는 부분의 제네릭 타입으로 둔다는 의미의 사용법입니다. 즉, T라는 타입의 싱글톤 클래스라는 뜻이죠.

T라는 타입은 CustomSingleton02도 될 수 있고 CustomSingleton03이 될 수도 있습니다. 아래와 같이 이용할 수 있습니다.

 public class CustomSingleton02 : CustomSingleton<CustomSingletoe02>
 {

 }
    
 public class CustomSingleton03 : CustomSingleton<CustomSingletoe02>
 {

 }

이렇게 제네릭 타입을 이용하는 클래스를 상속시켜주면, 상속받은 녀석이 타입을 결정할 수 있게 되는 것입니다.

참고로 where T : MonoBehaviour은 제약조건을 설정하는 부분입니다. 제네릭 타입의 경우, 타입의 범위를 제약해주지 않으면 제대로 사용할 수 없습니다. 제네릭하게 사용할 타입이 ValueType인지 RefType인지조차 모를 테니 말이죠.

 

public class CustomSingleton <T> : MonoBehaviour where T : MonoBehaviour

을 해석해 보자면, '싱글톤 객체를 생성할 것인데 타입은 MonoBehaviour타입에 한정된다.'입니다.


마무리

 일반적으로 제네릭의 관한 설명을 보면, 아직 모양이 정해지지 않은 틀에 비유를 많이 합니다. 적절한 설명인 것 같습니다. 아직 타입이 어떤 모양일지 정해지지 않아, 나중에 반죽하겠다는 뜻으로 해석하면... 괜찮은 설명 같습니다.

반응형
LIST

'Programming language > C#' 카테고리의 다른 글

[C#]닷넷(.Net)의 구성 요소  (0) 2022.08.09
[C#]닷넷(.Net)에 관하여  (0) 2022.08.07
[C#]컬렉션(Collection)  (0) 2022.04.19
[C#]가비지 컬렉터(Garbage collector)  (0) 2022.04.12
[C#]Boxing/Unboxing (박싱/언박싱)  (0) 2022.04.12