본문 바로가기
C#/[루키스] 실전 문법

[C# 섹션 8] Reflection

by 묻공러 2025. 6. 26.

# reflection

c#에서 만들어지는 모든 클래스는

최상위 타입인 object를 암묵적으로 상속받는다

그래서 GetType, GetHashCode, Equals 등의 메서드를 사용할 수 있다

 

특히, GetType을 통해

필드 정보(멤버변수, 멤버함수)를 모두 확인 및 추출이 가능하다

이처럼 클래스에 대한 모든 정보들을 런타임에 확인이 가능한 것이 reflection이다

C++ 에는 이러한 기능이 없다

using System;
using System.Reflection;
using System.Security.Cryptography;

namespace CSharp
{
    class Monster
    {
        public int hp;

        protected int attack;
        
        private float speed;

        void Attack() { }
    }
    
    internal class Program
    {
        static void Main(string[] args)
        {
            Monster monster = new Monster();
            Type type = monster.GetType();

            var fields = type.GetFields(System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic
                | System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Instance);

            foreach(FieldInfo field in fields)
            {
                string access = "protected";
                if (field.IsPublic)
                    access = "public";
                else if (field.IsPrivate)
                    access = "private";

                Console.WriteLine($"{access} {field.FieldType.Name} {field.Name}");
                // public Int32 hp
                // protected Int32 attack
                // private Single speed
            }
        }
    }
}

 

# attribute

주석을 런타임에 확인할 수 있는 기능으로

결국 주석을 데이터로 포함시키는 것과 같다

사용하는 예시는 아래와 같다

using System;
using System.Reflection;
using System.Security.Cryptography;

namespace CSharp
{
    class Important : System.Attribute // Attribute 상속 받은 클래스 생성
    {
        string msg;
        
        public Important(string msg) { this.msg = msg; }
    }
    
    class Monster
    {
        [Important("체력과 관련 변수입니다")] // Attribute 작성
        public int hp;

        protected int attack;
        
        private float speed;

        void Attack() { }
    }
    
    internal class Program
    {
        static void Main(string[] args)
        {
            Monster monster = new Monster();
            Type type = monster.GetType();

            var fields = type.GetFields(System.Reflection.BindingFlags.Public
                | System.Reflection.BindingFlags.NonPublic
                | System.Reflection.BindingFlags.Static
                | System.Reflection.BindingFlags.Instance);

            foreach(FieldInfo field in fields)
            {
                var attributes = field.GetCustomAttributes(); // 해당 변수에 주석이 함께 저장 됨
            }
        }
    }
}

 

# 유니티의 reflection과 attribute

두 기능 모두 툴에서 많이 사용된다

 

유니티에서 클래스를 만들고 public 멤버변수를 작성하면

런타임으로 바로 유니티에 해당 멤버변수를 확인 및 수정할 수 있는데

그 이유가 해당 reflection 기능 덕분이다

 

또한, 멤버변수를 private으로 갑자기 바꿔도

필드 접근지정자를 런타임에서 확인하기에 바로 숨겨준다

 

private 멤버변수라고 하더라도

[SerializedField]를 private 멤버변수 위에 작성하면

엔진에서 확인 및 수정이 가능한데

그 이유가 attribute 기능 덕분이다

[SerializedField]
public int hp;

 

 

이처럼 유니티 Inspector에 변수가 표시되는 이유는

reflection과 attribute 기능으로 가능한 것이다

reflection을 통해 변수의 접근 지정자를 확인하고,

attribute(SerializeField)로 Inspector 노출 여부를 결정하는 것이다

 

# 언리얼과 C++의 reflection

언리얼은 C++로 reflection 기능이 없기에

컴파일타임에 리플렉션 매크로(ex. UCLASS(), UPROPERTY())를 통해서

엔진 전용 메타데이터를 코드에서 추출하고,

별도의 리플렉션 테이블을 생성해서 필드를 확인한다

 

해당 테이블은 컴파일 타임에 생성되고,

런타임에 엔진이 접근할 수 있도록 준비한다
따라서, 필드 추가/변경 시 유니티와는 다르게 실시간 자동으로 즉시 인지하지 못하고,

빌드가 필요하다

참고로 언리얼 라이브 코딩으로

실시간 자동 기능이 있는 것이 아닌가 생각할 수 있다

그러나 라이브 코딩은 reflection과는 다른 별개의 기술이다

 

라이브 코딩은 코드 변경을 빠르게 컴파일하고, 

실행 중인 프로세스에 핫리로딩하는 기능인 것이다

 

좀 더 상세하게는
모듈 단위로 동적 라이브러리(.dll)를 분리해서 관리하고
변경된 모듈만 부분 컴파일 후, 실행 중인 게임에 교체를 한다
그리고 Visual Studio 빌드 시스템을 언리얼이 커스텀해서 지원한다

이렇게 해서 좀더 빠른 빌드를 제공하는 것이지 refelction이 적용된 것이 아니다

'C# > [루키스] 실전 문법' 카테고리의 다른 글

[C# 섹션 8] Nullable  (0) 2025.06.26
[C# 섹션 8] Exception  (2) 2025.06.26
[C# 섹션 8] Lambda  (0) 2025.06.25
[C# 섹션 8] Event  (0) 2025.06.25
[C# 섹션 8] Delegate  (0) 2025.06.25