C# - 리플렉션

윤형·2024년 10월 24일
0

Unity

목록 보기
1/8

리플렉션

리플렉션이란 프로그램이 실행되는 동안 자신의 구조를 검사하고 수정할 수 있는 기능을 의미합니다. 런타임에 대한 정보를 얻고 동적으로 조작할 수 있게 해줍니다.

  • 클래스, 매서드, 속성, 필드 등의 메타데이터 조회
  • 동적으로 메서드 호출
  • 객체의 속성 및 필드에 접근
  • 타입의 인스턴스 생성

주요 네임스페이스

System.Reflaction

이 네임스페이스는 리플렉션 관련 클래스와 메서드를 포함하고 있습니다.

주요 클래스

  • Type : 클래스의 메타데이터를 나타냅니다. 객체의 타입정보를 얻을 수 있습니다.
  • MethodInfo : 메서드에 대한 정보를 제공합니다. 메서드의 이름, 반환 타입, 매개변수 등을 확인할 수 있습니다.
  • PropertyInfo : 속성에 관한 데이터를 제공합니다. 속성의 이름, 타입, 접근자 등을 확인할 수 있습니다.
  • FieldInfo : 필드에 대한 정보를 제공합니다. 필드의 이름, 타입 등을 확인할 수 있습니다.
  • Activator : 타입의 인스턴스를 동적으로 생성할 수 있습니다.

리플렉션 사용 예제

1. 타입 정보 조회

using System;

public class MyClass
{
    public int MyProperty { get; set; }
    
    public void MyMethod()
    {
        Console.WriteLine("MyMethod called");
    }
}

class Program
{
    static void Main()
    {
        Type type = typeof(MyClass);
        
        // 클래스 이름 출력
        Console.WriteLine("Class Name: " + type.Name);
        
        // 속성 정보 출력
        foreach (var prop in type.GetProperties())
        {
            Console.WriteLine("Property: " + prop.Name);
        }
        
        // 메서드 정보 출력
        foreach (var method in type.GetMethods())
        {
            Console.WriteLine("Method: " + method.Name);
        }
    }
}
  • 타입 정보를 조회하면 그 메서드에 있는 메타데이터나 상태를 알 수 있다.

  • GetProperties() : 해당 타입의 모든 속성 정보를 배열 형태로 반환한다. 속성은 클래스의 데이터 멤버로, 일반적으로 get, set 접근자를 통해 값을 읽고 쓸 수 있다.(get과 set이 없는 속성은 일반적으로 존재X)

    • 반환 값: PropertyInfo[] 배열, 각 요소는 PropertyInfo객체로 속성의 이름, 타입, 접근자(getter,setter)등의 메타데이터를 포함한다.
  • GetMethods() : 해당 타입의 모든 메서드(Method) 정보를 배열 형태로 반환한다. 메서드는 클래스의 동작을 정의하는 함수로, 특정 작업을 수행한다.

    • 반환 값: MethodInfo[] 배열, 각 요소는 MethodInfo 객체로, 메서드의 이름, 반환 타입, 매개변수 정보, 접근자 등의 메타데이터를 포함한다.

2. 동적 메서드 호출

using System;
using System.Reflection;

public class MyClass
{
    public void SayHello()
    {
        Console.WriteLine("Hello, World!");
    }
}

class Program
{
    static void Main()
    {
        MyClass myObject = new MyClass();
        Type type = myObject.GetType();
        
        // SayHello 메서드 정보 가져오기
        MethodInfo methodInfo = type.GetMethod("SayHello", Type.EmptyTypes)!;

        if (methodInfo == null)
        {
            Console.WriteLine("메서드를 찾을 수 없습니다.");
            return;
        }else{
             // 메서드 호출
            methodInfo.Invoke(myObject, null);
        }
    }
}
  • GetType()메서드는 typeof()와는 다르게 해당 객체의 런타임 타입 정보를 가져온다.

  • GetMethod()는 특정 이름을 가진 단일 메서드의 정보를 반환한다.

  • GetMethod(,,,)은 null을 반환할 수 있기 때문에 null인지 확인하는 조건문이 필요하고, 경고 문구가 나오게 된다. 경고 문구는 "!"를 통해 명시적으로 null이 나오지 않는다고 컴파일러에게 알린다.

3. 속성 및 필드 접근

using System;
using System.Reflection;

public class MyClass
{
    public int MyProperty { get; set; }
}

class Program
{
    static void Main()
    {
        MyClass myObject = new MyClass();
        Type type = myObject.GetType();
        
        // 속성 정보 가져오기
        PropertyInfo propertyInfo = type.GetProperty("MyProperty");
        
        // 속성 값 설정
        propertyInfo.SetValue(myObject, 42);
        
        // 속성 값 가져오기
        int value = (int)propertyInfo.GetValue(myObject);
        Console.WriteLine("MyProperty: " + value);
    }
}
  • SetValue(obj, value) : 속성 값을 설정할 객체의 인스턴스와 설정할 값을 인자로 한다. 특정 객체의 속성 값을 설정하는 역할을 한다.

장점

  1. 유연성: 런타임에 타입을 동적으로 조작할 수 있기 때문에 유연한 프로그래밍이 가능해진다.
  2. 동적 타입 검사: 컴파일 타임에 알 수 없는 타입에 대해 정보를 얻을 수 있습니다. 이는 동적 코딩에 유리하다.

단점

  1. 성능 저하: 당연하게도 일반적으로 메서드를 호출하는 것보다 속도 저하가 발생한다.
  2. 안정성: 컴파일 타임에 타입 검사를 할 수 없으므로, 런타임 오류가 발생할 가능성이 높아집니다.
  3. 코드 가독성: 코드가독성이 떨어져 유지보수성을 떨어뜨릴 수 있다.
  4. 보안성: 비공식적인 접근이 가능해지므로 취약점이 될 수 있다.

유니티에서 활용

using System;
using System.Reflection;
using UnityEngine;

public class ReflectionExample : MonoBehaviour
{
    void Start()
    {
        // 호출할 메서드 이름과 매개변수
        string methodName = "MyMethod";
        object[] parameters = { 42, "Hello" }; // 예시 매개변수

        // 메서드 호출
        CallMethod(methodName, parameters);
    }

    void CallMethod(string methodName, object[] parameters)
    {
        // 현재 클래스의 Type 가져오기
        Type type = this.GetType();

        // 메서드 정보 가져오기
        MethodInfo methodInfo = type.GetMethod(methodName);

        if (methodInfo != null)
        {
            // 메서드 호출
            methodInfo.Invoke(this, parameters);
        }
        else
        {
            Debug.LogError($"Method '{methodName}' not found.");
        }
    }

    // 예시 메서드 (매개변수 있음)
    public void MyMethod(int number, string message)
    {
        Debug.Log($"MyMethod called with number: {number} and message: {message}");
    }
}

이런식으로 특정 문자열과 같은 이름을 가진 메서드를 실행시킬 수 있다.

내가 사용했던 방식을 설명하자면

  1. 각 카드의 능력을 switch-case문으로 나눴다.
  2. 카드가 적을때는 괜찮았지만 100장이 넘어가자 코드도 계속해서 늘어났다.
  3. 유지보수가 어렵다고 생각했다.
  4. 카드의 정보를 저장한 데이터 베이스에 카드를 사용하면 실행되는 메서드를 string형태로 같이 저장했다.
  5. 리플렉션을 이용해 메서드를 실행시켰다.

+) 사실 Invoke()만 써도 이름을 통해 메서드 실행이 가능하다...
+) 리플렉션은 직접 인스펙터나 에디터 도구를 만들때, 동적으로 객체의 정보를 가져올때 효율적으로 작동할 수 있다.

profile
제가 관심있고 공부하고 싶은걸 정리하는 저만의 노트입니다.

0개의 댓글