만약 아래와 같은 코드가 있다고 해보자
using System; public static class GenericTest { public static void ConsolePrint(int value) { Console.WriteLine(value); } public static void ConsolePrint(string text) { Console.WriteLine(text); } } public class _01Generic { static void Main() { float py = 3.141592f; GenericTest.ConsolePrint(py); } }
IDE로 가져와서 붙여넣어보면 py에 오류가 뜰것이다.
float에서 int로 변환이 불가하다고.
하지만
public static void ConsolePrint(int value)
이 부분을 제네릭함수로 변경해보자
public static void ConsolePrint<T>(T value) { Console.WriteLine(value); }
이렇게 함수의 식별자(함수명) 뒤에 <다양한 자료형을 대표할 임의의 이름>과
인자값을 자료형으로 명시하는것이 아닌 그 이름으로 해주면 된다.
제네릭함수는 자료형을 변수처럼 사용하고 싶을때 사용한다.
리턴값도 제네릭으로 변경가능하다
public static T ConsolePrint<T>(T value) { Console.WriteLine(value); return value; }
이제 어떻게 바뀌었나 살펴보자
using System; public static class GenericTest { public static T ConsolePrint<T>(T value) { Console.WriteLine(value); return value; } } public class _01Generic { static void Main() { GenericTest.ConsolePrint(1000000000000); //정수 GenericTest.ConsolePrint(3.33333333333); //실수 GenericTest.ConsolePrint("난 천재인가?"); //문자 } }
출력!
이렇게 함수하나만 만들어줬더니 그 함수가 모든걸 처리해 버렸다!
이것을 Generic이라고 한다!
이렇게 자동적으로 형식을 지정해준다.
generic을 사용하는 이유는 간단하다!
개발자가 편하려고! 코드를 조금 덜치려고!
public static class GenericTest { //1 public static T ConsolePrint<T>(T value) { Console.WriteLine(value); return value; } //2 public static IamIronMan ConsolePrint<T, IamIronMan>(T value, IamIronMan value2) { Console.WriteLine(value); Console.WriteLine(value2); return value2; } }
아래와 같이 2개의 식별자뒤에 2개의 이름을 적어줬다!
일단 반환값을 IamIronMan으로 바꿔주었고, 식별자 뒤와 인자값으로 IamIronMan을 추가해주었다.
여기서 알수있는점은
과연 출력은 어떻게 변하게 될까?
GenericTest.ConsolePrint(10000000, "IamIronMan");
일단 출력하는 코드를 위와같이 바꿔보았다
이럴수가! 출력이 잘만 된다!
게다가 형식도 자동으로 지정해준다!
GenericTest.ConsolePrint<int, string>(10000000, "IamIronMan");
보다싶이 int형과 string으로 명시적으로 호출해주었다.
이런식으로 명시적으로 호출이 가능하다
class CashItem { } class EquipItem { } class Inven { CashItem[] arrInvenItem; EquipItem[] arrInvenItem; }
이렇게 2개의 장비와 관련된 인벤토리와 1개의 총괄인벤토리가 있다고 가정해보자
오류가 분명히 나온다. 같은 변수명을 사용하기때문에
이럴때 사용하는게 제네릭클래스이다
using System; class CashItem { } class EquipItem { } class Inven<T> { T[] arrInvenItem; public void ItemIn(T item) { } } public class _01Generic { static void Main() { //새로운 장비 인벤토리 생성 Inven<EquipItem> NewEquipItemInven = new Inven<EquipItem>(); //장비아이템 객체생성 EquipItem NewEquipItem = new EquipItem(); //새로운 장비 인벤토리의 ItemIn함수에 객체가된 장비아이템을 넣는다. NewEquipItemInven.ItemIn(NewEquipItem); Inven<CashItem> NewCashItemInven = new Inven<CashItem>(); CashItem NewCashItem = new CashItem(); NewCashItemInven.ItemIn(NewCashItem); } }
제네릭 클래스를 사용하는 이유?
위와같이 여러 종류의 아이템을 인벤토리에 저장을 시켜야 된다고 가정해보자
제네릭이 없이는
CashItem[] arrCashItem, EquiItem[] arrEquipItem처럼 일일히 해당 배열을 지정해주고 거기에 알맞게 객체를 생성해서 넣어줘야 할것이다하지만 제네릭 클래스를 이용한다면 해당제네릭클래스에 여러다른타입들이 들어오더라도 각 기능에 알맞게 재사용 될수있게 도와준다!
한마디로 개발자가 편해진다!
작동방식을 이해해보자.
일단 class Inven 라는 제네릭 클래스를 만든다
해당 클래스에 어떻게 값들을 저장할 것인지 고민하여 배열을 만들어준다
객체로 만들어진 NewEquipItemInven는 T[] arrInvenItem의 배열으로 자동으로 변환되어 들어가게 된다.
3-1 마찬가지로 NewCashItemInven도 T[] arrInvenItem의 배열으로 들어가게 된다.
이후 생성된EquipItem의 NewEquipItem라는 객체는 EquipItem타입으로 지정되어 있다
이후 ItemIn이라는 메서드를 거쳐, NewEquipItem라는 객체는 NewEquipItemInven이라는 배열에 저장되게 된다
궁금점! 그럼 배열이 따로따로 만들어지면
만약에 장비템을 먹고있다가 갑자기 캐쉬를 질러서 캐쉬템을 사고 다시 사냥을 시작해 장비를 얻으면 어떻게 되는거지?
들어오는 장비분류에 따라서 타입이 변경되므로 각 다른 배열 NewEquipItemInven, NewCashItemInven에 알아서 저장되게 된다!
이와같이 가장 상위의 분류가 필요한 클래스는 제네릭으로 지정하여 타입분류를 자동으로 지정되게 해주면 개발자가 편하다!