Ssssong += Dev

[유니티, C#] 리플렉션(Reflection) 본문

개발/공부

[유니티, C#] 리플렉션(Reflection)

ssong_dev 2022. 6. 22. 11:38

타입의 결정을 런타임 시점으로 미뤄둘 수 있는 패턴이다.

컴파일 시점에서 위에 더 생길 클래스를 알지 못할 때  사용한다. 타입에 대한 정보를 가져와서 이후 처리한다.

 

GetType()은 단순히 데이터타입만을 의미하는 것이 아니라 해당 데이터타입이 가지고 있는 정보를 의미한다.

Temp temp로 생성된 객체를 temp.GetType() 으로 정보를 가져온다면 Temp가 가진 멤버도 가져올 수 있다.

 

FieldInfo[] fields = type.GetFields()로 해당 데이터타입이 가진 멤버 변수들을 가져올 수 있다.

BindingFlag를 사용하면 가져오는 멤버 변수의 성격 또한 지정할 수 있다.

 

Type type = slime.GetType();
type.GetField("hp").GetValue(slime);

위 코드는 slime의 hp를 가져온다. type은 slime의 데이터타입 자체를 담고 있기 때문에 slime 자체의 정보는 담고 있지 않다.

따라서 'slime의 타입으로 slime의 정보를 불러오겠다.' 라는 의미로 .GetValue(slime)을 해 주어야 한다.

 

GetValue과 반대로 SetValue는 값을 세팅한다.

type.GetField("hp").SetValue(slime, 80); 이런 형식으로 정보를 담는다.

단, 여기서 GetField("hp")의 형식이 어떻게 될 지 모르므로 이렇게 80으로 바로 적는 데에는 문제가 있다.

 

type.GetMethod("Attack").Invoke(slime, null);

 

데이터타입이 가진 함수를 가져온다. Invoke(객체, 매개변수)로 실행할 수 있다.

매개 변수가 없을 때에는 null을 넣으면 되지만 매개변수가 있을 때 매개변수의 갯수를 알 수 없다면 어떻게 해야 하는가?

 

이런 상황을 위해 매개변수를 object 배열로 넣어준다.

object[] args = new object[2];
args[0] = 10;
args[1] = 3;

type.GetMethod("Attack").Invoke(slime, args);

*parameter : 매개변수, argument : 실제로 들어가는 인자

 

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using System;

public class Temp
{
    public int tempValue;

    public int TempValue
    {
        get { return tempValue; }
        set { tempValue = value; }
    }

    public void FuncParamater()
    {
        Debug.Log("어떠한 기능");
    }
}

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

        temp.tempValue = 10;
        Debug.Log("타입의 이름 : " + temp.GetType().Name);

        Type type = temp.GetType(); // temp의 데이터타입 그 자체. temp의 정보는 없음

        //type.GetFields 멤버 변수 가져옴
        //type.GetPropertys 멤버 프로퍼티 가져옴
        MethodInfo[] methods = type.GetMethods(); //멤버 함수 가져옴

        int count = 0;
        foreach(var method in methods)
        {
            Debug.Log(count + "번째의 메서드의 이름 : " + method.Name);
            count++;
        }

        Debug.Log(type.GetField("tempValue").GetValue(temp)); //type이 데이터타입 그 자체이므로 temp 값을 알려면 temp를 다시 넣어줘야 한다.
        type.GetField("tempValue").SetValue(temp,30);

        type.GetMethod("FuncParamater").Invoke(temp, null); //null 자리에 매개변수 넣어야 할 경우 매개변수 들어감. 지금은 void라 null이 들어갔다.
    }
    
}

 

 

리플렉션에 더해 DeepCopy 간소화 코드(예외 경우 많이 생략됨)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using System;

public static class ExtensionMethod
{
    public static T DeepCopy<T>(this T obj) where T : new()
    {
        Type type = obj.GetType();
        if(type.IsClass)
        {
            T clone = new T();

            //클래스 멤버들 가져오기
            FieldInfo[] fields = type.GetFields();

            //멤버마다 복사
            foreach(FieldInfo field in fields)
            {
                field.SetValue(clone, (field.GetValue(obj)).DeepCopy());
            }

            return (T)clone;
        }

        //멤버가 복사가 되어야 하는 내용
        return (T)obj;
    }
}