[C#] 52. Reflection 기능을 사용하는 방법 - Property와 event


Study/C#  2021. 10. 19. 20:47

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


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


C#의 Reflection에 대해서 Class와 Method, Variable(변수)에 대해서 설명했었습니다.

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

link - [C#] 50. Reflection 기능을 사용하는 방법 - Method

link - [C#] 51. Reflection 기능을 사용하는 방법 - Variable


Reflection은 클래스의 구성요소에 대한 동적으로 데이터를 취득 및 호출하는 방법입니다.

Java에서의 Class의 구성요소가 보통 함수와 맴버 변수 밖에 없지만 C#에서는 프로퍼티와 이벤트 등의 특수 기능(?)을 함수가 있습니다.

프로퍼티는 함수 기능과 변수의 기능을 합친 요소이고 이벤트는 함수를 복수로 중첩하여 호출할 수 있는 기능입니다.

link - [C#] 20. 프로퍼티 (Property)

link - [C#] 24. 이벤트(event) 키워드 사용법


먼저 프로퍼티의 Reflection은 함수의 Reflection과 많이 비슷합니다.

using System;
using System.Reflection;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 맴버 변수
    private int data;
    // 프로퍼티
    public int Data
    {
      // set 함수
      set
      {
        // 콘솔 출력
        Console.WriteLine("Data property set");
        // 맴버 변수에 값을 입력
        this.data = value;
      }
      // set 함수
      get
      {
        // 콘솔 출력
        Console.WriteLine("Data property get");
        // 맴버 변수의 값을 리턴
        return this.data;
      }
    }
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 인스턴스 생성
      var node = new Node();
      // Node 클래스의 type을 취득
      Type type = typeof(Node);
      // 프로퍼티 Data의 Reflection 정보를 취득
      PropertyInfo property = type.GetProperty("Data");
      // Data 프로퍼티 set 함수를 호출, 값을 10 입력
      property.SetValue(node, 10);
      // Data 프로퍼티 get 함수를 호출하여 값을 취득
      var value = property.GetValue(node);
      // 콘솔 출력
      Console.WriteLine(value);

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

위 예제는 Node 클래스의 Data 프로퍼티의 set 함수와 get 함수를 호출하는 Reflection입니다.

프로퍼티 자체가 OOP를 위한 get set 함수이기 때문에 함수의 Reflection과 크게 다른게 없습니다. 프로퍼티도 private를 설정할 수 있고, static로 설정할 수 있지만, 보통은 public으로 설정하고 public이 아니라도, 함수 Reflection과 같이 BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static의 옵션으로 취득할 수 있습니다.


event의 경우는 함수를 중첩시켜서 한번에 호출하는 방식입니다.

using System;
using System.Reflection;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 델리게이트
    public event EventHandler ExecuteEvent;
  }
  class Program
  {
    // 실행 함수
    static void Main(string[] args)
    {
      // 인스턴스 생성
      Node node = new Node();
      // Node 클래스의 type을 취득
      Type type = typeof(Node);
      // ExecuteEvent의 Event reflection를 취득
      EventInfo eventInfo = type.GetEvent("ExecuteEvent");
      // 이벤트에 추가 시킬 함수
      MethodInfo methodInfo = typeof(Program).GetMethod("Print_Event", BindingFlags.NonPublic | BindingFlags.Static);
      // 함수를 Delegate로 변환
      Delegate d = Delegate.CreateDelegate(eventInfo.EventHandlerType, null, methodInfo);
      // 이벤트에 추가
      eventInfo.AddEventHandler(node, d);
      // 이벤트에 추가
      eventInfo.AddEventHandler(node, d);
      // 이벤트에 추가
      eventInfo.AddEventHandler(node, d);
      // ExecuteEvent 이벤트에 대한 실행 필드 취득
      var backingField = typeof(Node).GetField("ExecuteEvent", BindingFlags.Instance | BindingFlags.NonPublic);
      // node 인스턴스로 부터 핸들러 취득
      var handler = (EventHandler)backingField.GetValue(node);
      // 실행
      handler("Execute", new EventArgs());

      // 아무 키나 누르시면 종료합니다.
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
    // 콘솔 출력 이벤트
    static void Print_Event(object sender, EventArgs e)
    {
      // 콘솔 출력
      Console.WriteLine(sender);
    }
  }
}

위 예제에서는 GetEvent 함수를 통해서 Node 클래스의 event를 취득합니다.

이벤트에서는 Delegate 형식으로 이벤트를 추가하게 되는데, Delegate.CreateDelegate를 통해서 함수를 델리게이트(함수 포인터) 형식으로 변환해야 합니다.

그리고 다시 event reflection를 통해서 이벤트를 추가합니다. 저는 3번을 추가 했습니다.


사실 이벤트는 클래스 내부에서 실행해야 합니다. 하지만 그렇게 하면 Reflection이 아니죠. 외부에서 event를 호출할 수 있게 실행 Field 데이터를 취득해야 합니다. 이 부분이 재미있는게 여기에서는 Node 클래스에서 event를 멤버 변수처럼 작성했는데 멤버 변수로 인식을 합니다.

접근 제한자가 public인데 private로 인식이 됩니다. 즉, Field를 취득해서 EventHandler의 델리게이트 형식으로 강제 캐스팅을 하면 함수처럼 호출이 가능합니다.


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


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