C# - Reflection

MINO·2025년 9월 9일
0

C#

목록 보기
3/7
post-thumbnail

Reflection

프로그램 실행 중에 타입 정보를 확인하고, 동적으로 조작할 수 있는 기능.

런타임에 클래스, 메서드, 필드 등의 정보를 가져오고 호출할 수 있다.

using System.Reflection; // namespace 필수

internal class Program
{
    static void Main(string[] args)
    {
    	Console.WriteLine("Reflection");
        
        string str = "Test_Method";
        
        /* Reflection 활용하여 str 을 통해 Test_Method 를 호출할 수 있다. */
        
    }
    
    static void Test_Method()
    {
    	...
    }
}

Reflection - 타입 정보 얻기

Reflection 의 typeof() 와 GetType() 을 사용해 타입 정보를 확인할 수 있다.

typeof()

컴파일 타임에 특정 타입의 System.Type 객체를 가져온다.

  • 타입 이름을 직접 코드에 적어야 한다.
  • 인스턴스가 없어도 사용 가능하다.
Type t = typeof(string);
Console.WriteLine(t.FullName); // System.String

GetType()

런타임에 실제 객체의 타입 정보를 가져온다.
-반드시 인스턴스가 있어야 호출 가능하다.

string s = "Hi";
Type t = s.GetType();
Console.WriteLine(t.FullName); // System.String

활용 예시 1) GetType() , typeof()

using System.Reflection;

namespace ConsoleApp1
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Example 1 : Reflection - Type");
            Console.WriteLine();

            // Class 자체의 타입 정보를 가져오기
            Type t = typeof(Program);
            Console.WriteLine($"Type Name : {t.Name}");
            Console.WriteLine();

            Console.WriteLine($"Type FullName : {t.FullName}");
            Console.WriteLine();
            Console.WriteLine($"Type Namespace: {t.Namespace}");
            Console.WriteLine();
            Console.WriteLine();

            // Array 의 실제 인스턴스인 arr 의 런타임 타입을 가져오기
            int[] arr = { 1, 2 };
            Type arr_t = arr.GetType();
            Console.WriteLine($"Type Name : {arr_t.Name}");
            Console.WriteLine(); 
            Console.WriteLine($"Type FullName : {arr_t.FullName}");
            Console.WriteLine();
            Console.WriteLine($"Type Namespace: {arr_t.Namespace}");
        }
    }
}

실행 결과 : Reflection 에서 Type 객체를 얻고,
타입 객체의 Name, FullName, Namespace 를 출력한다.



Reflection - 멤버 정보 얻기

Type 객체를 얻은 뒤, 해당 타입이 가진 메서드, 프로퍼티, 필드, 생성자 등을 확인할 수 있다.

GetMethods()

클래스에 정의된 메서드들의 정보를 가져온다.

  • System.Reflection.MethodInfo[] 를 반환한다.

GetProperties()

클래스에 정의된 프로퍼티 정보를 가져온다.

  • System.Reflection.PropertyInfo[] 를 반환한다.

활용 예시 2) GetMethods() , GetProperties()

using System.Reflection;

namespace ConsoleApp1
{
    internal class Program
    {
        class Enemy
        {
            public string Name { get; set; }
            public int Level { get; set; }
        }


        static void Main(string[] args)
        {
            Console.WriteLine("Example 2 : Reflection - Members");
            Console.WriteLine();

            Type t = typeof(Program);

            Console.WriteLine("\n=== Program's Methods ===\n");

            MethodInfo[] methods = t.GetMethods();

            foreach (var method in methods)
            {
                Console.WriteLine($"{method.Name} (ReturnType: {method.ReturnType})");
            }

            Type e = typeof(Enemy);
            Console.WriteLine("\n=== Enemy's Properties ===\n");

            PropertyInfo[] properties= e.GetProperties();

            foreach (var property in properties)
            {
                Console.WriteLine($"{property.Name} ({property.PropertyType})");
            }

        }

        public void Test_Method()
        {
            Console.WriteLine("Test Method");
        }
    }
}

실행 결과 : Program 클래스에 정의된 public 메서드인 Test_Method 뿐 아니라,
ToString, Equals, GetHashCode, GetType 같은 .NET 기본 메서드들도 함께 출력한다.

Enemy 클래스의 Property 인 Name 과 Level 의 이름과 타입을 출력한다.



GetFields()

클래스에 정의된 필드 정보를 가져온다.

  • System.Reflection.FieldInfo[] 를 반환한다.

GetConstructors()

클래스의 생성자 정보를 가져온다.

  • System.Reflection.ConstructorInfo[] 를 반환한다.

활용 예시 3) GetFields() , GetConstructors()

using System.Reflection;

namespace ConsoleApp1
{
    internal class Program
    {
        class Enemy
        {
            public string name;
            private int level;
            private float health;

            public Enemy() { }
            public Enemy(string name, int level, float health)
            {
                this.name = name;
                this.level = level;
                this.health = health;
            }
        }


        static void Main(string[] args)
        {
            Console.WriteLine("Example 3 : Reflection - Members");
            Console.WriteLine();

            Enemy e = new Enemy();
            Type t = e.GetType();

            Console.WriteLine("\n=== Fields ===\n");
            FieldInfo[] fields = t.GetFields(BindingFlags.Public
            | BindingFlags.NonPublic | BindingFlags.Instance);

            foreach (var field in fields)
            {
                Console.WriteLine($"{field.Name} ({field.FieldType}) - {field.Attributes}");
            }

            Console.WriteLine("\n=== Constructors ===\n");
            ConstructorInfo[] constructors = t.GetConstructors();
            
            foreach (var constructor in constructors)
            {
                Console.WriteLine(constructor.ToString());
            }
        }
    }
}

실행 결과 : 필드의 이름과 타입, 접근 지정자를 반환한다.
매개변수 없는 기본 생성자와 (string name, int level, float health) 를 받는 생성자 모두 반환된다.

C# 에서 생성자 정보를 Reflection 으로 확인했을 때 float 타입이 Single 로 출력되는 이유는
CLR(Common Language Runtime) 의 내부 타입 이름 때문이라고 한다.
(float → System.Single / bool → System.Boolean / long → System.Int64)



Reflection - BindingFlags

멤버를 검색할 때 범위와 조건을 지정하는 열거형

GetField(), GetMethods(), GetProperties() 은 기본적으로 public 인스턴스 멤버만 반환한다.

원하는 멤버만 가져오기 위해서 BindingFlags 를 통해 범위와 조건을 지정해주면 된다.


플래그설명
BindingFlags.Publicpublic 멤버만 검색
BindingFlags.NonPublicprivate, protected, internal 멤버 검색
BindingFlags.Instance인스턴스 멤버 검색
BindingFlags.Static정적 멤버 검색
BindingFlags.DeclaredOnly상속받은 멤버 제외, 현재 타입에 선언된 것만 검색
  • | 연산자로 여러 플래그를 조합 가능하다.
  • 기본값 없이 사용하면 public 인스턴스만 가져온다.
FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

활용 예시

키-값 쌍으로 이루어진 csv 파일이나 텍스트 파일을 읽어와
해당 타입에 맞게 자동 파싱을 해서 넣을 수 있는 기능을 만들어 볼 수 있다.

using System.Reflection;

namespace ConsoleApp1
{
    internal class Program
    {
        class ItemTable
        {
            public int ItemId { get; set; }
            public string Name { get; set; }
            public string Desc { get; set; }
        }


        static void Main(string[] args)
        {
            Console.WriteLine("Example 4 : Reflection");
            Console.WriteLine();

            string[] keys = { "ItemId", "Name", "Desc" };
            string[] values = { "1", "검", "초보자용 기본 검" };

            ItemTable itemTable = new ItemTable();
            PropertyInfo[] propertyInfos = itemTable.GetType().GetProperties();

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                Console.WriteLine($"{propertyInfo.PropertyType}, {propertyInfo.Name}");

                for (int i = 0; i < keys.Length; i++)
                {
                    if (keys[i].ToLower().Equals(propertyInfo.Name.ToLower()))
                    {
                        if (propertyInfo.PropertyType == typeof(int))
                        {
                            propertyInfo.SetValue(itemTable, int.Parse(values[i]));
                        }
                        else
                        {
                            propertyInfo.SetValue(itemTable, values[i]);
                        }
                    }
                }
            }

            Console.WriteLine($"\nItemId : {itemTable.ItemId} , ItemName : {itemTable.Name} , 
            Desc : {itemTable.Desc}");
        }
    }
}

실행 결과 : int 형인 ItemId 는 Parse 를 통해 string 을 int 로 바꿔서 넣어주고,
Name 과 Desc 는 string 값으로 입력되었다.


profile
안녕하세요 게임 개발하는 MINO 입니다.

0개의 댓글