개발언어/C#

[C#] 리플렉션(Reflection)

내꺼블로그 2024. 5. 8. 14:47

리플렉션(Reflection)

리플렉션이란 런타임 중에 객체의 형식(클래스, 인터페이스, 값 형식)에 대한 정보를 가져올 수 있는 기능을 의미한다. 리플렉션을 사용하면 동적으로 형식을 가져올 수 있고, 형식에 대한 인스턴스를 생성할 수 있다. 런타임 내에서 동적으로 기능하기 때문에 성능상 좋지 않다는 단점을 가지고 있다. 주로 테스트, 디버그 용도로 사용한다.(테스트가 끝나면 관련 부분은 주석처리 해주는 것이 좋다.) 리플렉션을 사용하려면 네임스페이스로 System.Reflection을 추가해야 한다.

 


 

GetType

GetType()이란 이름 그대로 해당 type 개체를 가져오는 메서드이다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace TestReflection
{
    public class Student
    {
        public int id;
        public string name;
        public int age;
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student();
            Type type = student1.GetType();
            Console.WriteLine(type);
        }
    }
}

 

GetType()을 사용하여 student1의 타입을 가져오는 코드를 작성해보았다.

 

결과

 

 

참고로 Type 개체를 가져오는 메서드로 typeof()도 있다. typeof()의 파라미터로는 가져올 타입을 적으면 된다. 결과는 위의 GetType()을 썼을 때와 같은 결과가 나온다.

 


 

GetConstructor, GetMethod, GetMember, GetField ...

GetType 외에도 Type 클래스에서 사용하는 여러 메서드들이 있다. 다양한 메서드들은 아래의 링크에서 참고할 수 있다.

https://learn.microsoft.com/ko-kr/dotnet/api/system.type?view=net-8.0#methods

 

GetConstructor(Type[])

지정된 배열의 형식과 일치하는 매개 변수를 가진 public 생성자를 반환하는 메서드이다. ConstructorInfo를 반환하며 해당 생성자를 찾지 못했을 경우 null을 반환한다.

 

GetConstructors()

현재 타입에 정의된 모든 public 생성자들을 반환하는 메서드이다. ConstructorInfo[]를 반환하며 생성자를 찾지 못했을 경우 빈 배열을 반환한다.

 

GetMethod(string)

지정된 이름의 public 메서드를 반환하는 메서드이다. MethodInfo를 반환하며 해당 메서드를 찾지 못했을 경우 null을 반환한다.

 

GetMethods()

현재 타입의 모든 public 메서드들을 반환하는 메서드이다. MethodInfo[]를 반환하며 메서드를 찾지 못했을 경우 빈 배열을 반환한다.

 

GetMember(string)

지정된 이름의 public 멤버를 반환하는 메서드이다. MemeberInfo[]를 반환하며 해당 멤버를 찾지 못했을 경우 빈 배열을 반환한다.

 

GetMembers(string)

현재 타입의 모든 public 멤버들을 반환하는 메서드이다. MemeberInfo[]를 반환하며 멤버를 찾지 못했을 경우 빈 배열을 반환한다.

 

GetField(string)

지정된 이름의 public 필드를 반환하는 메서드이다. FieldInfo를 반환하며 해당 필드를 찾지 못했을 경우 null을 반환한다.

 

GetFields()

현재 타입의 모든 public 필드를 반환하는 메서드이다. FieldInfo[]를 반환하며 필드를 찾지 못했을 경우 빈 배열을 반환한다.

 

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace TestReflection
{
    public class Student
    {
        public int id;
        public string name;
        public int age;
        
        public Student() { }

        public Student(int id, string name, int age)
        {
            this.id = id;
            this.name = name;
            this.age = age;
        }

        public void Study()
        {
            Console.WriteLine("study...");
        }

        public void Attend()
        {
            Console.WriteLine("attend...");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student();

            ConstructorInfo[] constructorInfos = student1.GetType().GetConstructors();
            foreach(var info  in constructorInfos)
            {
                Console.WriteLine("constructor: {0}", info);
            }

            Console.WriteLine();

            MethodInfo[] methodInfos = student1.GetType().GetMethods();
            foreach(var info in  methodInfos)
            {
                Console.WriteLine("method: {0}", info);
            }

            Console.WriteLine();

            MemberInfo[] memberInfos = student1.GetType().GetMembers();
            foreach(var info in memberInfos)
            {
                Console.WriteLine("member: {0}", info);
            }

            Console.WriteLine();

            FieldInfo[] fieldInfos = student1.GetType().GetFields();
            foreach(var info in  fieldInfos)
            {
                Console.WriteLine("field: {0}", info);
            }
            Console.WriteLine();
        }
    }
}

 

 

결과

Student에 해당하는 각각의 생성자, 메서드, 멤버, 필드 정보를 얻은 모습을 확인할 수 있다.

 

 

 

함수호출

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace TestReflection
{
    public class Student
    {
        public int id;
        public string name;
        public int age;
        
        public Student() { }

        public Student(int id, string name, int age)
        {
            this.id = id;
            this.name = name;
            this.age = age;
        }

        public void Study()
        {
            Console.WriteLine("study...");
        }

        public void Attend()
        {
            Console.WriteLine("attend...");
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student();
            MethodInfo info1 = student1.GetType().GetMethod("Attend");
            MethodInfo info2 = student1.GetType().GetMethod("Play");

            if(info1 != null )
            {
                info1.Invoke(student1, null);
            }

            Console.WriteLine("=================================");

            if(info2 != null )
            {
                info2.Invoke(student1, null);
            }
           
        }
    }
}

 

결과

해당 클래스에 존재하는 Attend 메서드만 호출된 모습을 확인할 수 있다.

Play 메서드는 정의되지 않았으므로 호출되지 않았다.

 


 

GetValue, SetValue

GetValue와 SetValue를 사용하여 필드의 데이터를 가져오거나 수정할 수 있다.

아래의 코드는 GetValue로 홍길동의 데이터를 가져오고, SetValue를 통해 홍길동이 나이를 한 살 더 먹은 것을 구현하고 있다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace TestReflection
{
    public class Student
    {
        public int id;
        public string name;
        public int age;
        
        public Student() { }

        public Student(int id, string name, int age)
        {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            Student student1 = new Student(20151234, "홍길동", 27);
            FieldInfo[] fieldInfos = student1.GetType().GetFields();
            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                Console.WriteLine($"{fieldInfo.Name}: {fieldInfo.GetValue(student1)}");
            }

            Console.WriteLine();

            var ageInfo = fieldInfos[2];
            var age = ageInfo.GetValue(student1);
            ageInfo.SetValue(student1, (object)(Convert.ToInt32(age) +1));

            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                Console.WriteLine($"{fieldInfo.Name}: {fieldInfo.GetValue(student1)}");
            }
        }
    }
}

 

 

결과

GetValue를 통해 필드 정보를 가져오고, SetValue를 통해 필드 정보가 수정된 모습을 볼 수 있다.

 

 


 

System.Activator.CreateInstance

System.Activator.CreateInstance(Type)을 사용하면 해당 타입의 인스턴스를 생성할 수 있다.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;

namespace TestReflection
{
    public class Student
    {
        public int id;
        public string name;
        public int age;
        
        public Student() { this.id = 20240001; this.name = "홍길동"; this.age = 20; }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            var type = Type.GetType("TestReflection.Student");
            var student = Activator.CreateInstance(type);
            FieldInfo[] fieldInfos = student.GetType().GetFields();
            foreach (FieldInfo fieldInfo in fieldInfos)
            {
                Console.WriteLine($"{fieldInfo.Name}: {fieldInfo.GetValue(student)}");
            }
        }
    }
}

 

 

결과

System.Activator.CreateInstance를 사용하여 Student 타입 인스턴스를 생성한 모습이다.