C#
리플렉션과 어트리뷰트
khkg12
2024. 1. 2. 15:42
리플렉션이란?
클래스의 메타데이터(변수, 함수, 프로퍼티 등)을 런타임에서 가져와 사용할 수 있는 기능이다. 리플렉션은 탐색 과정을 거치기 때문에 연산이 많이 들기 때문에 이 점을 유의하며 사용해야 한다.
어트리뷰트란?
속성을 부여하는 것으로 자주사용되는 [SerializeField] 어트리뷰트 또한 직렬화라는 속성을 어트리뷰트가 붙여진 변수에 부여하는 것이다.
예시 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using System;
// 객체 : 속성과 기능을 가진 것, 멤버는 속성과 기능 전부를 뜻함
public class Monster
{
public string name;
private int hp;
private int maxhp;
private float atk;
public int Hp
{
get => hp;
set { hp = value; }
}
public void Attack()
{
Debug.Log(name + $"이 공격");
}
public void Hit()
{
Debug.Log("몬스터 맞았다");
Hp -= 10;
}
}
public class Npc
{
public string name;
public void Attack()
{
Debug.Log(name + "은 공격하지 않는다.");
}
public void Hit()
{
Debug.Log("맞았다");
}
}
public class GameManager : MonoBehaviour
{
// 리플렉션
// Reflection : 메타적인 데이터를 가져오는 문법
public Box box;
Monster monsterA;
Monster monsterB;
void Start()
{
monsterA = new Monster();
monsterB = new Monster();
monsterA.Hp = 100;
monsterA.name = "슬라임";
monsterB.Hp = 300;
Type type = monsterA.GetType();
type = monsterB.GetType();
type = typeof(Monster);
// 셋 다 몬스터라는 클래스에 대한 정보를 가져오는 것으로, 셋 다 동일
Debug.Log(type.Name);
MemberInfo[] minfos = type.GetMembers(); // private은 안가져옴
minfos = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); // 퍼블릭인 놈과 논퍼블릭, 인스턴스 멤버인 애들을 다가져옴
// 위처럼 가져올때(바인드를 엮어줄때) public만 쓰면 안됨. 그놈이 인스턴스 영역인지 스태틱 영역인지도 알려주어야함.
// object가 가지고 있는 GetType, .ctor같은 것도 다 나옴.
FieldInfo hpFieldInfo = type.GetField("hp", BindingFlags.NonPublic | BindingFlags.Instance);
hpFieldInfo.SetValue(monsterB, 50); // 값을 누구의 것을 변경할지 정해주고, 어떤값으로 바꿀껀지 넣어줌
Debug.Log(hpFieldInfo.GetValue(monsterA)); // monsterA 객체의 hp를 가져옴
Debug.Log(hpFieldInfo.GetValue(monsterB)); // monsterB 객체의 hp를 가져옴
MethodInfo attackMethodInfo = type.GetMethod("Attack");
attackMethodInfo.Invoke(monsterA, null);
//foreach (MemberInfo minfo in minfos)
//{
// Debug.Log(minfo.Name);
//}
//Debug.Log(monsterA.GetType()); // 위 아래 동일함
//Debug.Log(monsterB.GetType());
// monsterA.GetType().Hp; <- 불가능, 타입에 대한 정보를 가져오는것이지 그 자체가 아님, 몬스터 객체 그자체가 아닌, 그정보를 담고있는 것일 뿐
Npc npc = new Npc();
npc.name = "상인";
string methodName = "Hit";
Attack(monsterA, methodName); // Invoke나 StartCourtine을 쓸 때의 과정과 동일함.
Attack(npc, methodName);
Attack(box, methodName);
}
public void Attack(object obj, string methodName)
{
Type type = obj.GetType();
MethodInfo attackMethodInfo = type.GetMethod(methodName);
attackMethodInfo?.Invoke(obj, null); // 예외처리 그 함수이름이 없다면 못찾아서 null이 될테니 실행안됨
}
}