-
제네릭 완벽하게 이해하기Unity, C#/Generic 2019. 3. 6. 17:19
제목대로 제네릭에 대해서 알아보고자 합니다.실무 코드에서도 많이 사용하는것 같습니다.테스트 환경은 유니티입니다.
제네릭
클래스나 함수에 사용합니다.함수를 만들 때 입력값 타입을 정하지 않고, 함수를 호출할 때 입력값 타입을 정해서 호출합니다.클래스에서 사용할 타입을 클래스를 만들 때 정하지 않고, 클래스를 사용할 때 정해서 사용합니다.일반적으로 <T> 라고 표현합니다. T는 다른 문자가 들어가도 상관없습니다만 대부분 T 를 씁니다.
T : 형식매개변수(Type Parametor)
어떤 점이 좋을까요?
아래 예제를 보시면 이해가 빠를것 같습니다.
EX)제네릭을 함수에 적용 예
Print() 함수를 사용해서 int형 30 과 string형 "Hello World" 를 출력하고자 합니다.Generic을 사용하지 않을 때
123456789101112131415void 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을 사용할 경우123456789101112void 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> 를 예를 들수 있겠습니다.
1234567891011121314151617181920212223242526272829303132void 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 라는 배열에는 원하는 타입의 배열을 만들수가 있게 됩니다.
실무에서 사용되는 코드를 보겠습니다.
123456789101112131415161718192021222324252627282930313233343536public class GameSceneManager : Singleton<GameSceneManager>{private GameScene _pCurrentGameScene = null;public GameScene CurrentGameScene { get { return _pCurrentGameScene; } }}public class GameScene{}public class Singleton<T> where T : class, new(){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
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788public class GenericTest : MonoBehaviour{public static void TestDictionary(){Dictionary<string, string> genDic = new Dictionary<string, string>();genDic.Add("txt", "notepad.exe");genDic.Add("bmp", "paint.exe");genDic.Add("mp3", "foobar.exe");Debug.Log("[Dictionary]");foreach (KeyValuePair<string, string> 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
1234567891011121314151617181920212223242526272829303132class 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
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859class 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
123456789101112131415161718192021222324252627282930313233public 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 응용
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960class 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