제네릭 리플렉션 (Generic Reflection)

2022. 1. 28. 00:00CSharp/Advance

반응형

리플렉션의 관점에서 제네릭 형식과 일반 형식 간 차이점

  • 형식 매개 변수(제네릭 형식 정의인 경우) 의 집합과 연결되어 있다.
  • 형식 인수(생성된 형식인 경우)의 집합과 연결되어 있다

리플렉션을 사용하여 Type  의 instance 에서 알 수 없는 형식을 검사할때는 IsGenericType 속성을 사용한다. 

리플렉션을 사용하여 MethodInfo  의 instance 에서 알 수 없는 메소드를 검사할때는 IsGenericMethod 속성을 사용한다. 

 

Type 개체가 제네릭 형식인지 알기 위해서 IsGenericTypeDefinition 을 사용한다. 

 

Type.ContainsGenericParameters 속성이 true 라면 설정되지 않는 Type 이 Generic class 에 존재 한다는 것이다. 이말은 Reflection 을 통해 GenericType 을 생성할때 아직 Type 을 지정하지 않은 parameter 가 있다는 것이므로 이럴경우 generic instance 를 생성할 수 없다. 

 

Dictionary<TKey,TValue> 에서  TKey 를 지정하고 TValue 를 지정하지 않은 상태일 수 있다는 것이다.

 

Dictionary<string, TValue>  이런식

Method.ContainsGenericParameters 도 같은 형태이다.

 

GenericType 또는 GenericMethod 를 reflection 으로 instance 를 생성해서 실행하기 위해서는 폐쇄형(parameter 가 실제형식이 지정되어야 한다. ) 이되어야 한다. 

 

public class MyStack<T>
{
    T[] items = { };
    int index;

    public virtual U GenericMethod<U>(T item) where U:T
    {
        return (U)item;
    }
    public float Method(float item)
    {
        return item;
    }
    public T GetItem(int index) => items[index];
    public void AddItem(T item)=> items = items.Append(item).ToArray(); 
}
public class MyStack
{
    object[] items;
    int index;
}

// 폐쇄형 class
public class IntMyStack<T> : MyStack<int>
{
    public override U GenericMethod<U>(int item)
    {
        return base.GenericMethod<U>(item);
    }
    public virtual T TParamterMethod(T item)
    {
        return item;
    }
}

public class Test : ITest
{
    public void Run()
    {
        MyStack<float> genericStack = new MyStack<float>();
        MyStack stack = new MyStack();
        IntMyStack<float> intMyStack = new IntMyStack<float>();

        Console.WriteLine($"genericStack is generic type == {genericStack.GetType().IsGenericType}");
        Console.WriteLine($"stack is generic type == {stack.GetType().IsGenericType}");


        //foreach (var method in typeof(MyStack<>).GetMethods())
        foreach (var method in genericStack.GetType().GetMethods())
            Console.WriteLine($"{method.Name} is generic method == {method.IsGenericMethod}, is open generic method == {method.ContainsGenericParameters}");

        Console.WriteLine($"typeof(IntMyStack<>) is opened generic type == {typeof(IntMyStack<>).ContainsGenericParameters}");
        Console.WriteLine($"intMyStack is opened generic type == {intMyStack.GetType().ContainsGenericParameters}");

        // 이것은 IntMyStack<> 이라는 type 에대한 비교 이므로 완전 폐쇄형이 아니다.
        // 그러므로 아래 intMyStack 이라는 instance 와 method 에 대한 ContainsGenericParameters 값이 다르다.
        //foreach (var method in typeof(IntMyStack<>).GetMethods())
        foreach (var method in intMyStack.GetType().GetMethods())
            Console.WriteLine($"{method.Name} is opened generic method == {method.ContainsGenericParameters}");

        var myStackType = typeof(MyStack<>);
        var type_MyStack_Float =myStackType.MakeGenericType(typeof(float));
        var instance = Activator.CreateInstance(type_MyStack_Float);
        (instance as MyStack<float>).AddItem(12.3f);
        Console.WriteLine((instance as MyStack<float>).GetItem(0));
    }
}

 

intMyStack 같은 경우는 IntMyStack<float> 라는 형식을 지정 하여

 IntMyStack<float> intMyStack = new IntMyStack<float>();

IntMyStack<T> 의 T = float

 

 MyStack<int> 상속하며 형식을 지정하여

public class IntMyStack<T> : MyStack<int>

MyStack 의 T = int

그러므로 intMyStack 은 폐쇄형 (모든 Type 이 지정되었다. int, float) 이다.

 

그러므로 intMyStack.GetType() 와 typeof(IntMyStack<>) 의  TParameterMethod 의 ContainsGenericParameters 값이 다르다.

 

typeof(IntMyStack<>).GetMethods().TParameterMethodContainsGenericParameters 값은 true 이다.

 

intMyStack.GetType().GetMethods().TParameterMethodContainsGenericParameters 값은 false 이다.

 

MakeGeneric 를 통해 instance 를 생성하는 부분은 아래 코드 이다. 

var myStackType = typeof(MyStack<>);
var type_MyStack_Float =myStackType.MakeGenericType(typeof(float));
var instance = Activator.CreateInstance(type_MyStack_Float);
(instance as MyStack<float>).AddItem(12.3f);
Console.WriteLine((instance as MyStack<float>).GetItem(0));

 

관련영상

https://youtu.be/P3KF9G0vWsg

 

 

 

반응형

'CSharp > Advance' 카테고리의 다른 글

LINQ 제한 연산자 (Where)  (0) 2022.02.01
LINQ 란  (0) 2022.01.31
제네릭 대리자 (Generic Delegate)  (0) 2022.01.27
제네릭 메서드 (Generic Method)  (0) 2022.01.26
제네릭 클래스 (Generic class)  (0) 2022.01.25