[C#] 49. Reflection 기능을 사용하는 방법 - Class


Study/C#  2021. 10. 13. 18:00

안녕하세요. 명월입니다.


이 글은 C#에서 Reflection 기능을 사용하는 방법 - Class에 대한 글입니다.


Reflection 기능이란 인스턴스를 동적으로 할당하거나 함수나 필드 및 속성에 동적으로 호출할 수 있는 기능이라고 설명은 되어있습니다.

링크 - https://docs.microsoft.com/

이게 설명이 어렵네요. 인스턴스를 동적으로 할당한다라는 뜻은 우리가 클래스의 인스턴스를 생성한다고 할 때, 보통 new라는 키워드를 사용합니다.

using System;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 프로퍼티
    public int Data { get; set; }
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine(Data);
    }
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 인스턴스 생성
      Node node = new Node();
      // 프로퍼티에 데이터 입력
      node.Data = 10;
      // 콘솔 출력 함수 호출
      node.Print();
      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

클래스의 인스턴스는 기본적으로 new를 이용해서 생성한다라는 건, 이전에 충분히 설명했습니다.

링크 - [C#] 10. 인스턴스 생성(new)과 메모리 할당(Stack 메모리와 Heap 메모리) 그리고 null


인스턴스를 생성할 때, 소스에 new Node라고 작성합니다. Reflection에서는 이걸 정적 표현이라고 하네요.

즉, 데어터나 분기에 따른 다른 인스턴스를 생성하고 싶으면 if를 사용해서 소스에 작성을 해야 합니다.

using System;

namespace Example
{
  // 인터페이스
  interface INode
  {
    // 추상 함수
    void Print();
  }
  // 예제 클래스
  class Node1 : INode
  {    
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Node1");
    }
  }
  // 예제 클래스
  class Node2 : INode
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Node2");
    }
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 임의의 데이터
      string input = "Node1";
      // 인스턴스 생성
      INode node;
      if ("Node1".Equals(input))
      {
        // Node1 클래스의 인스턴스 생성
        node = new Node1();
      }
      else
      {
        // Node2 클래스의 인스턴스 생성
        node = new Node2();
      }
      // 콘솔 출력 함수 호출
      node.Print();
      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

위 예제를 보면 type이라는 임의의 데이터를 두어서 Node1이면 Node1의 인스턴스를 생성하고 그 외에는 Node2의 인스턴스를 생성합니다.

근데 여기서 사양에 따라 Node3의 클래스가 생긴다면? if를 더 분기를 해서 인스턴스 생성하는 구간을 만들게 될 것입니다. 물론 클래스가 생기면 생길수록 Main 함수가 계속 수정이 필요하겠습니다.

using System;

namespace Example
{
  // 인터페이스
  interface INode
  {
    // 추상 함수
    void Print();
  }
  // 예제 클래스
  class Node1 : INode
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Node1");
    }
  }
  // 예제 클래스
  class Node2 : INode
  {
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Node2");
    }
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // Node2의 Type을 취득
      Type type = Type.GetType("Example.Node2");
      // 인스턴스 생성
      INode node = (INode)Activator.CreateInstance(type);
      // 콘솔 출력 함수 호출
      node.Print();
      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

이런 식으로 만들게 되면 클래스가 생성되더라도 Type.GetType의 String 값에 따라 인스턴스가 생성됩니다.


또 우리가 클래스의 생성자를 private로 생성할 수 있습니다.

생성자를 private로 설정을 하게 되면 new로 인스턴스를 생성할 수 없습니다. 생성자가 내부에서만 생성할 수 있게 설정되어서 그렇죠.. 대표적으로 싱글톤 패턴이 있습니다.

링크 - [Design Pattern] 1-1. 싱글톤 패턴 (Singleton pattern)


그런데 Reflection을 사용하게 되면 private로 설정이 되어 있어도 인스턴스 생성이 가능합니다.

using System;
using System.Reflection;
using System.Linq;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 생성자를 print로
    private Node()
    {

    }
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine("Node");
    }
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // Node2의 Type을 취득
      Type type = Type.GetType("Example.Node");
      // 생성자 취득
      var constructor = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Where(x => x.GetParameters().Length == 0).First();
      // 인스턴스 생성
      dynamic node = constructor.Invoke(null);
      // 콘솔 출력 함수 호출
      node.Print();

      // 아무 키나 누르면 종료
      Console.WriteLine("Press Any key...");
      Console.ReadLine();
    }
  }
}

GetParameters 함수를 써서 생성자의 종류를 취득하고 where로 파라미터가 없는 생성자를 하나 취득합니다. 취득하면 Invoke 함수를 써서 인스턴스를 생성합니다.

즉, 파라미터가 있는 생성자는 파라미터의 개수나 타입으로 취득할 수 있고 생성도 가능합니다.


여기까지 C#에서 Reflection 기능을 사용하는 방법 - Class에 대한 글이었습니다.


궁금한 점이나 잘못된 점이 있으면 댓글 부탁드립니다.