C# - 제네릭 인자를 가진 타입을 생성하는 방법
제네릭 인자를 가진 타입의 경우에도 역시 Reflection에서 인스턴스를 생성할 수 있습니다. 단지, 제네릭 인자를 제공해야 하므로 이를 위해 MakeGenericType 메서드를 이용해야 합니다.
Type.MakeGenericType 메서드 (Type[])
; https://learn.microsoft.com/en-us/dotnet/api/system.type.makegenerictype
그래서 다음과 같은 정도로 코딩을 할 수 있습니다.
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
class Program
{
public string Name { get; set; }
static void Main(string[] args)
{
Program pg = new Program();
pg.Name = "TEST";
Type type = typeof(List<>);
Type genericProgramType = type.MakeGenericType(new Type[] { pg.GetType() });
object objValue = Activator.CreateInstance(genericProgramType);
Console.WriteLine(objValue); // 출력: System.Collections.Generic.List`1[ConsoleApp1.Program]
}
}
}
참고로 아래의 질문을 보면,
리스트 아이템의 타입을 추출해서 다시 재활용하고 싶어요.
; https://www.sysnet.pe.kr/3/0/5007
형식 인자로 System.Type의 인스턴스를 넣었는데 당연히 컴파일 오류가 발생합니다.
Type argType = typeof(ListEnum);
List<argType> list; // 컴파일 에러: Error CS0118 'argType' is a variable but is used like a type
제네릭 타입의 형식 인자는 반드시 타입 이름을 적어주어야 합니다.
List<ListEnum> list;
따라서 위의 질문에서 나온 코드는 다음과 같이 구현할 수 있습니다.
using System;
using System.Collections.Generic;
namespace ConsoleApp1
{
enum ListEnum
{
A, B, C, D
}
class Program
{
static void Main(string[] args)
{
List<ListEnum> listA = new List<ListEnum>() { ListEnum.A, ListEnum.B };
var type = listA.GetType().GetGenericArguments()[0];
Console.WriteLine(type);
Type listType = typeof(List<>);
Type genericListType = listType.MakeGenericType(new Type[] { type });
object objValue = Activator.CreateInstance(genericListType);
Console.WriteLine(objValue);
}
}
}
그런데, 문제는 objValue를 형식 안정성이 부여된 List<T>로 형변환할 수 없다는 점입니다. 만약 형변환이 될 상황이라면 애당초 Reflection이 아닌 그냥 평범한 new 생성을 해주면 됩니다. 따라서 이후의 List<T> 사용에 대한 모든 메서드 호출을 Reflection으로 해야 합니다.
물론 Reflection은 너무 번거로우므로 dynamic을 쓰는 것도 괜찮은 선택입니다.
object objValue = Activator.CreateInstance(genericListType);
dynamic list = objValue;
list.Add(ListEnum.A);
Console.WriteLine(list[0]); // 출력: A
그런데, List의 제네릭 인자 타입이 예측 가능한 상황이라면 그냥 다음과 같이 써서 형식 안정성을 가지는 것이 더 좋습니다.
List<ListEnum> list = new List<ListEnum>() { ListEnum.A, ListEnum.B };
switch (list)
{
case List<ListEnum> _:
List<ListEnum> copy = new List<ListEnum>();
break;
default:
break;
}
어느 쪽을 쓰든 상황에 맞게 쓰면 되겠지요.
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]