ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 제네릭 완벽하게 이해하기
    Unity, C#/Generic 2019. 3. 6. 17:19

    제목대로 제네릭에 대해서 알아보고자 합니다.실무 코드에서도 많이 사용하는것 같습니다.테스트 환경은 유니티입니다.

     

     

    제네릭


    클래스나 함수에 사용합니다.함수를 만들 때 입력값 타입을 정하지 않고, 함수를 호출할 때 입력값 타입을 정해서 호출합니다.클래스에서 사용할 타입을 클래스를 만들 때 정하지 않고, 클래스를 사용할 때 정해서 사용합니다.일반적으로 <T> 라고 표현합니다. T는 다른 문자가 들어가도 상관없습니다만 대부분 T 를 씁니다.
    T : 형식매개변수(Type Parametor)
    어떤 점이 좋을까요?
    아래 예제를 보시면 이해가 빠를것 같습니다. 
    EX) 

    제네릭을 함수에 적용 예


    Print() 함수를 사용해서 int형 30 과 string형 "Hello World" 를 출력하고자 합니다.

     

    Generic을 사용하지 않을 때

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
        void Start()
        {
            Print(30);
            Print("Hello World");
        }
     
        public void Print(int inputMessage)
        {
            Debug.Log(inputMessage);
        }
     
        public void Print(string inputMessage)
        {
            Debug.Log(inputMessage);
        }
    cs

    위와 같이 Overloading 이나 Overriding 을 사용해서 입력값의 타입만큼 함수를 만들어야 합니다. 
     
     
    Generic을 사용할 경우

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
        void Start()
        {
            Print<int>(30);
            Print<string>("Hello World");
        }
     
     
        // T : 타입
        public void Print<T>(T inputMessage)
        {
            Debug.Log(inputMessage);
        }
    cs

     

    위와 같이 함수를 호출할 때 <T> 을 사용해서 입력값 타입을 지정해 줌으로써

    Print() 함수는 어떤 타입이라도 받을 수 있게 됩니다. 

    유니티 내장함수 중에는 GetComponent<RigidBody>() 가 있겠네요.

     

    코드가 많이 간결해 지겠지요. 

     

     

    제네릭을 클래스에 적용 예

     

    C# 클래스 중에는 List<string> 를 예를 들수 있겠습니다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
        void Start()
        {
            Container<string> container = new Container<string>();
            container.messages = new string[3];
     
            container.messages[0= "Hello";
            container.messages[1= "World";
            container.messages[2= "Generic";
     
            for (int i = 0; i < container.messages.Length; i++)
            {
                Debug.Log(container.messages[i]);
            }
     
            Container<int> container2 = new Container<int>();
            container2.messages = new int[3];
     
            container2.messages[0= 0;
            container2.messages[1= 1;
            container2.messages[2= 100;
     
            for (int i = 0; i < container2.messages.Length; i++)
            {
                Debug.Log(container2.messages[i]);
            }
        }
    }
     
    public class Container<T> 
    {
        public T[] messages;
    }
    cs

     

    Container 클래스에 보면 <T>가 있습니다. 즉 Container 클래스에서 사용할 타입이 정해져 있지 

    않습니다. 

    Container를 생성할 때 타입을 정합니다.  

    그렇기 때문에 messages 라는 배열에는 원하는 타입의 배열을 만들수가 있게 됩니다.

     

     

    실무에서 사용되는 코드를 보겠습니다.

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    public class GameSceneManager : Singleton<GameSceneManager>
    {
        private GameScene _pCurrentGameScene = null;
        public GameScene CurrentGameScene { get { return _pCurrentGameScene; } }
    }
     
    public class GameScene
    {
     
    }
     
    public class Singleton<T> where T : classnew()
    {
        public static T Instance
        {
            get;
            private set;
        }
     
        static Singleton()
        {
            if (Singleton<T>.Instance == null)
            {
                Singleton<T>.Instance = new T();
            }
        }
    }
     
     
    public class UserRewardNew
    {
        void Start()
        {
            GameScene _pInGameScene = GameSceneManager.Instance.CurrentGameScene;
        }
    }
    cs
     

    위의 코드는 저희 회사에서 사용하는 코드입니다.

    게임 코드에 보면 UIManager , SoundManager, GameSceneManager 등 많은 싱글톤이 있습니다. 여기서 싱글톤 생성하는 부분만

    따로 클래스로 만들어서 상속받아서 쓰고 있습니다. 

     

    코드 설명

    1. GameSceneManager 가 Singleton을 상속받고 있습니다. 

    2. T 형식매개 변수는 GameSceneManager 입니다.

    3. Instance == null 이면 Instance에 GameSceneManager 를 new로 생성해서 대입합니다.

     

     

    제네릭(Generic)에 대해 좀 더 다양한 예를 보겠습니다. 

     

    Generic Collection

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    public class GenericTest : MonoBehaviour
    {
        public static void TestDictionary()
        {
            Dictionary<stringstring> genDic = new Dictionary<stringstring>();
     
            genDic.Add("txt""notepad.exe");
            genDic.Add("bmp""paint.exe");
            genDic.Add("mp3""foobar.exe");
     
            Debug.Log("[Dictionary]");
            foreach (KeyValuePair<stringstring> kvp in genDic) 
            {
                Debug.Log(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
            }
        }
     
        public static void TestLinkedList()
        {
            LinkedList<string> genLL = new LinkedList<string>();
     
            genLL.AddLast("4등");
            genLL.AddFirst("1등");
            genLL.AddAfter(genLL.Find("1등"), "2등");
            genLL.AddBefore(genLL.Find("4등"), "3등");
     
            Debug.Log("[LinkedList]");
            foreach (string str in genLL) 
            {
                Debug.Log(string.Format("Value = {0}", str));
            }
        }
     
        public static void TestList()
        {
            List<string> genList = new List<string>();
     
            genList.Add("한국");
            genList.Add("중국");
            genList.Add("중국2");
            genList.Remove("중국2");
     
            Debug.Log("[List]");
            foreach (string str in genList)
            {
                Debug.Log(string.Format("Value = {0}", str));
            }
        }
     
        public static void TestQueue()
        {
            Queue<int> genQueue = new Queue<int>();
     
            genQueue.Enqueue(1);
            genQueue.Enqueue(2);
            genQueue.Enqueue(3);
     
            Debug.Log("[Queue]");
            for (int i = 0; i < 3++i) 
            {
                Debug.Log(string.Format("Value = {0}", genQueue.Dequeue()));
            }
        }
     
        public static void TestStack()
        {
            Stack<int> genStack = new Stack<int>();
     
            genStack.Push(1);
            genStack.Push(2);
            genStack.Push(3);
     
            Debug.Log("[Stack]");
            for (int i = 0; i < 3++i) 
            {
                Debug.Log(string.Format("Value = {0}", genStack.Pop()));
            }
        }
     
        void Start()
        {
            TestDictionary();
            TestLinkedList();
            TestList();
            TestQueue();
            TestStack();    
        }
    }
    cs

     

     

    Generic Class 1

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    class BaseData<T>
    {
        // default : 형식 매개 변수의 기본값을 지정합니다. 
        // 기본값은 참조 형식의 경우 null이고 값 형식의 경우 0입니다.
        private T data = default(T);
        public void SetData(T data)
        {
            this.data = data;
        }
     
        public T GetData()
        {
            return this.data;
        }
    }
     
    public class GenericTest : MonoBehaviour
    {    
        void Start()
        {
            string str = "Generic Test";
            BaseData<string> strData = new BaseData<string>();
            BaseData<object> objData = new BaseData<object>();
            BaseData<int> intData = new BaseData<int>();
            BaseData<double> doubleData = new BaseData<double>();
     
            strData.SetData(str);
            string str2 = strData.GetData();
     
            Debug.Log(str2);
        }
    }
    cs

     

     

    Generic Class 2

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    class Array4All<T>
    {
        private T[] arr;
        public Array4All(int size) // 생성자
        {
            arr = new T[size];
        }
     
        // 인덱서 선언
        public T this[int i]
        {
            get { return arr[i]; }
            set { arr[i] = value; }
        }
     
        public System.Collections.IEnumerator GetEnumerator()
        {
            for (int i =0;i<arr.Length;i++)
            {
                yield return arr[i];
            }
        }
    }
     
    public class GenericClassTest : MonoBehaviour
    {
     
        void Start()
        {
            Array4All<int> intArr = new Array4All<int>(5);  // 생성자 실행
            Array4All<double> doubleArr = new Array4All<double>(5);
            Array4All<string> strArr = new Array4All<string>(5);
     
            for (int i = 0; i < 5; i++
            {
                intArr[i] = i + 1;
                doubleArr[i] = i + .1;
                strArr[i] = "A" + i;
            }
     
            Debug.Log("[intArr]");
            foreach (int num in intArr) 
            {
                Debug.Log(num);
            }
     
            Debug.Log("[doubleArr]");
            foreach (double doubleNum in doubleArr)
            {
                Debug.Log(doubleNum);
            }
     
            Debug.Log("[strArr]");
            foreach (string str in strArr)
            {
                Debug.Log(str);
            }
        }
    }
    cs

     

     

    Generic Method

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class GenericMethodTest : MonoBehaviour
    {
        public void GenericMethod<T>(T value)
        {
            Debug.Log(value);
        }
     
        public T[] createArray<T>(int size, T initValue)
        {
            T[] arr = new T[size];
            for (int i = 0; i < size; i++)
            {
                arr[i] = initValue;
            }
     
            return arr;
        }
     
        void Start()
        {
            GenericMethod<int>(2007);
            GenericMethod<string>("잘 작동하는군요");
            GenericMethod<double>(3.14);
     
     
            string[] arr = createArray<string>(3"Generic Init");
            Debug.Log(string.Format("arr.length : {0}", arr.Length));
            foreach (string str in arr) 
            {
                Debug.Log(str);
            }    
        }
    }
    cs

     

     

    Generic 응용

     

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    class Array4All<T>
    {
        private T[] arr;
        public Array4All(int size) // 생성자
        {
            arr = new T[size];
        }
     
        // 인덱서 선언
        public T this[int i]
        {
            get { return arr[i]; }
            set { arr[i] = value; }
        }
     
        public System.Collections.IEnumerator GetEnumerator()
        {
            for (int i = 0; i < arr.Length; i++)
            {
                yield return arr[i];
            }
        }
    }
     
    public class GenericClassTest : MonoBehaviour
    {
     
        void Start()
        {
            Array4All<int> intArr = new Array4All<int>(5);  // 생성자 실행
            Array4All<double> doubleArr = new Array4All<double>(5);
            Array4All<string> strArr = new Array4All<string>(5);
     
            for (int i = 0; i < 5; i++)
            {
                intArr[i] = i + 1;
                doubleArr[i] = i + .1;
                strArr[i] = "A" + i;
            }
     
            Debug.Log("[intArr]");
            foreach (int num in intArr)
            {
                Debug.Log(num);
            }
     
            Debug.Log("[doubleArr]");
            foreach (double doubleNum in doubleArr)
            {
                Debug.Log(doubleNum);
            }
     
            Debug.Log("[strArr]");
            foreach (string str in strArr)
            {
                Debug.Log(str);
            }
        }
    }
     
    cs

     

    Generic 응용에서 

    " public System.Collections.IEnumerator GetEnumerator() "

    이게 없으면 에러가 나는데 왜 그런지 아직 잘 모르겠습니다.

    혹시 아시는 분 댓글로 설명 좀 부탁드립니다.

     

     

    출처 : https://blog.naver.com/thx4alice/110023499903

            https://adibong.tistory.com/entry/%ED%8E%8C-c-%EC%A0%9C%EB%84%A4%EB%A6%AD-%EC%99%84%EB%B2%BD%EC%A0%95%EB%A6%AC%ED%95%B4%EB%B3%B4%EC%9F%88

     

    댓글

Designed by Tistory.