Reflection

최정훈·2024년 10월 2일

💡 프로그램의 런타임 중에 코드에 대한 정보를 조사하고 조작할 수 있는 기능

Reflection을 사용하면 객체가 가지고 있는 모든 정보를 런타임에 동적으로 뜯어보고 분석할 수 있고, 정보를 얻어 직접 매서드를 호출하거나 프로퍼티를 변경할 수 있다.

  • 리플렉션을 사용하지 않았을 때 - 외부 클래스의 매서드와 변수에 접근하기 위해서 new 키워드를 통해서 객체를 생성해야 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Temp
{
    public int tempValue;
    public int TempValue
    {
        get { return tempValue; }
        set
        {
            tempValue = value;
        }
    }
    public void Func()
    {
        Debug.Log("어떠한 기능");
    }
}

public class ReflectionTest : MonoBehaviour
{
    private void Start()
    {
        Temp temp = new Temp();

        Debug.Log("타입이름 : " + temp);
    }
}
  • 리플렉션의 GetType을 사용한 경우
using System.Reflection // 필수적으로 추가

public class ReflectionTest : MonoBehaviour
{
    private void Start()
    {
        Temp temp = new Temp(); 
        
        Debug.Log("타입이름 : " + temp.GetType().Name);
    }
    // GetType을 하여 클래스의 이름을 출력한 상태
    // 클래스의 정보만을 가져온다
}

  • Reflection을 이용한 여러 Type함수들

GetFields() : 맴버 변수들을 가져옴
GetPropertys() : 맴버 프로퍼티를 가져옴
GetMethods() : 맴버 함수들을 가져옴


  • 사용예시
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System; 
using System.Reflection; 	// 필수!

public class Player
{
    public float currentHP;
    public string name;

    public void Test()
    {
        Debug.Log("Player의 매서드");
    }
}

public class ReflectionTest : MonoBehaviour
{
    private void Start()
    {
        Player player = new Player();
        
        Type type; // 데이터 타입의 정보가 담겨있는 Type변수

        type = player.GetType();  
        // player가 가지고 있는 데이터타입의 정보를 넘겨준다.
        // player의 변수와 매서드는 아직 모르는 상태.
        // player의 값이 아닌, 데이터 타입의 정보를 가져온 것.
                             
        // type.Test(); 사용불가
        // type에는 클래스 자체만을 넘겨주었기 때문에 Test()함수를 이용할 수 없다

        MethodInfo[] methos = type.GetMethods();
        // type에 들어있는 Player의 맴버함수들을 보관하는 배열 methodInfo
        foreach (MethodInfo method in methos)
        {
            Debug.Log("함수의 이름 : " + method.Name);
            // Player 클래스 안의 모든 함수들을 확인해본다.
        }

        FieldInfo[] fields = type.GetFields();
        // type에 들어있는 Player의 맴버변수들을 보관하는 배열 fields
        foreach (FieldInfo field in fields)
        {
            Debug.Log("멤버 변수의 이름 : " + field.Name);
            // Player 클래스 안의 모든 변수들을 확인해본다.
         }

        PropertyInfo[] properties = type.GetProperties();
        // type에 들어있는 Player의 프로퍼티들을 보관하는 배열 properties
        foreach (PropertyInfo property in properties)
        {
            Debug.Log("프로퍼티 이름 : " + property.Name);
            // Player 클래스 안의 모든 프로퍼티들을 확인해본다.
        }
    }
}

////////////// 출력
// Player의 매서드
// 함수의 이름 : Test
// 함수의 이름 : Equals (파생)     // c#의 모든 객체들은 Object에서 파생되어 나오기 때문에, Object에 정의된 함수 사용 가능
// 함수의 이름 : GetHashCode (파생) 
// 함수의 이름 : GetType (파생) 
// 함수의 이름 : ToString (파생) 
// 맴버 변수의 이름 : currentHP
// 맴버 변수의 이름 : name 

  • Get, Set을 통해서 함수 사용
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;               
using System.Reflection;    // 필수!

public class Player
{
    public float currentHP;
    public float currentMana;
    public string name;
}

public class ReflectionTest : MonoBehaviour
{
    private void Start()
    {
        Player player = new Player();
        Player player2 = new Player();

        player.currentHP = 100;  
        player2.currentHP = 300; // 생성자를 통한 변수 설정

        Type type;
        type = player.GetType(); // Player클래스의 정보를 type에 전달

        
        type.GetField("currentHP").SetValue(player, 400);
        type.GetField("name").SetValue(player, "First Player");
        type.GetField("currentMana").SetValue(player, 30.5f);
        // SetValue의 매개변수 타입은 오브젝트이기 때문에 박싱을 통해들어가 어떤 데이터타입도 SetValue로 들어갈 수 있다
        // 이 과정에서 내부에서 리플렉션이 일어나고 있는것!! 왜냐하면 박싱과 언박싱을하기 위해서는 클래스에 대한 메타 정보를 알고 있어야 하기 때문.

        Debug.Log(type.GetField("currentHP").GetValue(player));
        Debug.Log(type.GetField("currentHP").GetValue(player2));
        Debug.Log(type.GetField("currentMana").GetValue(player));
        // GetValue() 함수의 인자로 player를 안적어주면 GetValue 상태에서 어떤 녀석의 hp를 가져와줄지 모른다
        // 왜냐하면 GetField 만으로는 Class에대한 메타 정보만을 가져와 주기 떄문
    }
}

////////////// 출력
// 400
// 300
// 30.5

이때 주의할 것은, GetField만으로는 클래스의 객체(player, player2)에 대한 정보를 가져오는 것이 아닌, 데이터 타입의 정보만을 가져오게 된다. 그렇기 때문에, type = player.GetType(); 에서의 type만으로는 Player클래스에 만들어놓은 변수들을 불러오지 못한다. 그렇기 때문에, GetValue(player)이런 식으로 어떤 객체의 currentHP를 가져올 것인지 표현해 주어야 한다.

  • 요약
    • Reflection의 기능 클래스, 메서드, 속성 등의 정보를 동적으로 확인할 수 있다. 타입 정보를 기반으로 객체를 동적으로 생성할 수 있다 메서드 이름과 매개변수를 지정하여 실행할 수 있다 필드와 속성 값을 읽거나 변경할 수 있다 어셈블리의 정보를 확인할 수 있다.
    • 단점 런타임에 객체를 처리하는 과정에 오버헤드가 발생할 수 있다. 그렇기 때문에, 최대한 Reflection을 사용하지 않는 방향으로 설계하고, 빌드 후 디버그 할 때 사용하고, 디버그를 완료하면 주석처리
profile
게임개발자(희망)의 공부일지

0개의 댓글