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


Study/C#  2021. 10. 15. 14:03

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


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


이전 글에서 Reflection에 대해 클래스와 함수 사용법에 대해 설명했습니다.

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

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


Reflection이라는 것은 클래스나 함수에서 소스에 인스턴스 생성식(new)이나 함수 호출식(method())이 아닌 동적으로 Reflection 기능을 이용해 생성 및 호출하는 방법을 뜻합니다.

변수도 같은 내용입니다. 특히 C#에서는 이 클래스 안에서의 변수는 객체 지향 개발 방식(OOP)으로 인해 변수는 private로 생성하는 게 일반적입니다.

그러나 사양에 의해 강제적으로 변수의 값을 변경해야 하거나 유닛 테스트(NUnit)를 실시할 때 디버깅의 자료로서 사용하는 경우가 많이 있습니다.

using System;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 맴버 변수는 private
    private int data;
    // 생성자
    public Node(int data)
    {
      // 생성자에서 맴버 변수에 값을 넣는다.
      this.data = data;
    }
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine(data);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 인스턴스 생성
      var node = new Node(10);
      // 결과는 10
      node.Print();
      // 아무 키나 누르면 종료
      Console.WriteLine("Press any key...");
      Console.ReadKey();
    }
  }
}

위 예제는 아주 일반적인 코딩 방법으로 Node 클래스의 인스턴스를 생성할 때, 생성자에 10이라는 값을 넣어서 Print 함수를 통해 콘솔에 출력하였습니다.

당연히 Main 함수에서는 Node의 인스턴스의 맴버 변수인 data를 참조할 수 없습니다. 생성자에서 값을 넣는 것 이외에는 값을 변경하거나 값을 취득하지 못하고 Print함수를 통해 출력만 가능합니다.

using System;
using System.Reflection;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 맴버 변수는 private
    private int data;
    // 생성자
    public Node(int data)
    {
      // 생성자에서 맴버 변수에 값을 넣는다.
      this.data = data;
    }
    // 콘솔 출력 함수
    public void Print()
    {
      // 콘솔 출력
      Console.WriteLine(data);
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 인스턴스 생성
      var node = new Node(10);
      // Node 클래스의 Type을 취득
      var nodeClz = typeof(Node);
      // Node 클래스의 data 변수를 취득(private나 protected 타입 | 인스턴스타입(static이 아닌 맴버변수)
      var field = nodeClz.GetField("data", BindingFlags.NonPublic | BindingFlags.Instance);
      // 인스턴스 맴버 변수의 값을 취득
      var data = field.GetValue(node);
      // 콘솔 출력
      Console.WriteLine(data); ;
      // 인스턴스 맴버 변수의 값을 설정
      field.SetValue(node, 100);
      // 함수 호출
      node.Print();

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

위에서 typeof(Node)로 Node 클래스의 Type 구조를 취득한 후에 data의 맴버 변수를 취득했습니다.

그리고 콘솔에 출력을 하니 맴버 변수의 값이 출력되네요. 역시 SetValue를 통해 값을 설정하고 Print 함수를 호출하니 변경된 값이 출력이 되었습니다.


여기서 보면 Reflection를 통해 함수를 취득하는 경우와 매우 흡사합니다. 사실 똑같습니다.

using System;
using System.Reflection;

namespace Example
{
  // 예제 클래스
  class Node
  {
    // 프로퍼티
    public int Data
    {
      get; set;
    }
  }

  class Program
  {
    static void Main(string[] args)
    {
      // 인스턴스 생성
      var node = new Node();
      // Node 클래스의 Type을 취득
      var nodeClz = typeof(Node);
      // Node 클래스의 모든 변수를 취득
      foreach (var field in nodeClz.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
      {
        // 변수명과 값 출력
        Console.WriteLine($"{field.Name} = {field.GetValue(node)} ");
        // 변수명에 Data가 포함되어 있으면
        if (field.Name.Contains("Data"))
        {
          // 설정
          field.SetValue(node, 1000);
        }
      }
      // node 인스턴스의 프로퍼티 Data를 출력
      Console.WriteLine(node.Data);

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

위 예제는 Node 클래스에 맴버 변수가 없습니다. 단지 프로퍼티만 있습니다.

그런데 제가 Reflection를 통해서 Node 클래스의 변수를 취득하니 Data의 backingfield 라는 변수가 있네요. 즉, 프로퍼티에서 get, set으로 설정하면 컴파일해서 사용되면 자동으로 private 변수가 생성되는 것을 확인할 수 있습니다.

즉, 프로퍼티만 작성해도 내부에서는 private 맴버 변수가 생성되서 OOP 규약에 맞추는 것을 확인할 수 있네요.

값을 설정하고 프로퍼티로 값을 취득하니 값이 바뀌어 있는 것을 확인할 수 있습니다.


역시나 Reflection은 C#의 접근자를 무시하고 데이터를 취득하고 설정할 수 있습니다.

일반 프로그램을 작성할 때는 사용하기를 비추천합니다. 단지 NUnit 테스트나 디버깅 프로그램등을 만들 때 사용된다면 유용하겠네요.


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


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