Reflection을 이용해서 객체 생성

00·2024년 12월 29일

C#

목록 보기
94/149
using System;
using System.Reflection; // 리플렉션에 필요한 네임스페이스


/*
Reflection을 이용해서 객체 생성하기

Activator.CreateInstance() 메서드:
Reflection을 이용해서 '동적으로 인스턴스를 만들기' 위해 필요함.
인스턴스를 만들려는 형식의 Type 객체를 이 메서드의 매개변수에 넘기면,
입력받은 형식의 인스턴스를 생성하여 반환함.

PropertyInfo.SetValue() 메서드, 
PropertyInfo.GetValue() 메서드:
'동적으로 프로퍼티에 값을 기록하고 읽는'데 필요함.
 PropertyInfo 클래스는 Type.GetProperties()의 반환 형식임.
 PropertyInfo 객체는 SetValue()와 GetValue()라는 메서드를 갖고 있는데,
 GetValue()를 호출하면 프로퍼티로부터 값을 읽을 수 있고,
 SetValue()를 호출하면 프로퍼티에 값을 할당할 수 있습니다.

PropertyInfo 클래스는 프로퍼티뿐만 아니라 인덱서의 정보도 담을 수 있는데,
 GetValue()와 SetValue()의 마지막 인수는 인덱서의 인덱스를 위해 사용됩니다.
따라서 프로퍼티는 인덱서가 필요 없으므로 아래 코드에서 null로 할당한 것을 확인할 수 있습니다.
 */

namespace DynamicInstance
{
    class Profile // Profile 클래스 정의
    {
        private string name; // name 필드 정의
        private string phone; // phone 필드 정의

        public Profile() // 기본 생성자
        {
            name = ""; phone = ""; // name과 phone 필드를 빈 문자열로 초기화
        }

        public Profile(string name, string phone) // name과 phone을 매개변수로 받는 생성자
        {
            this.name = name; // 전달받은 name을 name 필드에 저장
            this.phone = phone; // 전달받은 phone을 phone 필드에 저장
        }

        public void Print() // 이름과 전화번호를 출력하는 메서드
        {
            Console.WriteLine($"{name}, {phone}");
        }

        public string Name // name 필드에 접근하기 위한 프로퍼티
        {
            get { return name; }
            set { name = value; }
        }

        public string Phone // phone 필드에 접근하기 위한 프로퍼티
        {
            get { return phone; }
            set { phone = value; }
        }
    }


    class MainApp
    {
        static void Main(string[] args)
        {
            Type type = Type.GetType("DynamicInstance.Profile");
            // DynamicInstance.Profile 클래스(Profile 클래스)의 타입 정보를 가져와 type 변수에 저장합니다.

            MethodInfo methodInfo = type.GetMethod("Print");
            // Print() 메서드의 정보를 가져와 methodInfo 변수에 저장합니다.

            PropertyInfo nameProperty = type.GetProperty("Name");
            // Name 프로퍼티의 정보를 가져와 nameProperty 변수에 저장합니다.

            PropertyInfo phoneProperty = type.GetProperty("Phone");
            // Phone 프로퍼티의 정보를 가져와 phoneProperty 변수에 저장합니다.

            object profile = Activator.CreateInstance(type, "박상현", "512-1234");
            // 'Activator.CreateInstance() 메서드'를 사용하여 Profile 클래스의 객체를 생성하고,
            // 생성자에 "박상현"과 "512-1234"를 인자로 전달합니다.
            // 생성된 객체는 profile 변수에 저장됩니다.

            methodInfo.Invoke(profile, null); // methodInfo 변수에 저장된 Print 메서드를, profile 객체에서 호출합니다.



            profile = Activator.CreateInstance(type); // Activator.CreateInstance() 메서드를 사용하여,
                                                      // Profile 클래스의 객체를 생성합니다.
                                                      // 이번에는 생성자에 인자를 전달하지 않으므로
                                                      // 기본 생성자가 호출됩니다.
            nameProperty.SetValue(profile, "박찬호", null); // nameProperty 변수에 저장된 Name 프로퍼티에
                                                         // "박찬호" 값을 설정합니다.      
            phoneProperty.SetValue(profile, "997-5511", null); // phoneProperty 변수에 저장된 Phone 프로퍼티에
                                                               // "997-5511" 값을 설정합니다.

            Console.WriteLine("{0}, {1}", 
                nameProperty.GetValue(profile, null),
                phoneProperty.GetValue(profile, null)); // Name 프로퍼티와 Phone 프로퍼티의 값을 가져와서 출력합니다.
                                                        // PropertyInfo 클래스는 Type.GetProperties()의 반환 형식.
                                                        // PropertyInfo 객체는 SetValue()와 GetValue()라는 메서드를 갖고 있는데,
                                                        // GetValue()를 호출하면 프로퍼티로부터 값을 읽을 수 있고,
                                                        // SetValue()를 호출하면 프로퍼티에 값을 할당할 수 있습니다.
        }
    }
}


/*
출력 결과

박상현, 512-1234
박찬호, 997-5511
 */

코드 설명

  • Profile 클래스: 이름과 전화번호를 저장하는 namephone 필드, 생성자, Print() 메서드, Name 프로퍼티, Phone 프로퍼티를 가진 클래스입니다.
  • MainApp 클래스:
    • Type type = Type.GetType("DynamicInstance.Profile");: DynamicInstance.Profile 클래스의 타입 정보를 가져와 type 변수에 저장합니다.
    • MethodInfo methodInfo = type.GetMethod("Print");: Print 메서드의 정보를 가져와 methodInfo 변수에 저장합니다.
    • PropertyInfo nameProperty = type.GetProperty("Name");: Name 프로퍼티의 정보를 가져와 nameProperty 변수에 저장합니다.
    • PropertyInfo phoneProperty = type.GetProperty("Phone");: Phone 프로퍼티의 정보를 가져와 phoneProperty 변수에 저장합니다.
    • object profile = Activator.CreateInstance(type, "박상현", "512-1234");: Activator.CreateInstance() 메서드를 사용하여 Profile 클래스의 객체를 생성하고, 생성자에 "박상현"과 "512-1234"를 인자로 전달합니다. 생성된 객체는 profile 변수에 저장됩니다.
    • methodInfo.Invoke(profile, null);: methodInfo 변수에 저장된 Print 메서드를 profile 객체에서 호출합니다.
    • profile = Activator.CreateInstance(type);: Activator.CreateInstance() 메서드를 사용하여 Profile 클래스의 객체를 생성합니다. 이번에는 생성자에 인자를 전달하지 않으므로 기본 생성자가 호출됩니다.
    • nameProperty.SetValue(profile, "박찬호", null);: nameProperty 변수에 저장된 Name 프로퍼티에 "박찬호" 값을 설정합니다.
    • phoneProperty.SetValue(profile, "997-5511", null);: phoneProperty 변수에 저장된 Phone 프로퍼티에 "997-5511" 값을 설정합니다.
    • Console.WriteLine("{0}, {1}", nameProperty.GetValue(profile, null), phoneProperty.GetValue(profile, null));: Name 프로퍼티와 Phone 프로퍼티의 값을 가져와서 출력합니다.

출력 결과

박상현, 512-1234
박찬호, 997-5511

코드 해석

이 코드는 리플렉션을 사용하여 객체를 동적으로 생성하고, 메서드를 호출하고, 프로퍼티 값을 설정하고 가져오는 방법을 보여줍니다. 리플렉션은 컴파일 시점에 타입 정보를 알 수 없는 경우 유용하게 사용할 수 있습니다.


Console.WriteLine("{0}, {1}", 
nameProperty.GetValue(profile, null),
phoneProperty.GetValue(profile, null)); 

이 부분은 profile 객체의 Name 프로퍼티와 Phone 프로퍼티 값을 가져와서 콘솔에 출력하는 코드입니다.

namePropertyphoneProperty는 각각 Name 프로퍼티와 Phone 프로퍼티의 정보를 담고 있는 PropertyInfo 객체입니다.

GetValue(object obj, object[] index) 메서드는 PropertyInfo 객체를 사용하여 특정 객체의 프로퍼티 값을 가져옵니다.

  • obj: 프로퍼티 값을 가져올 객체입니다. 여기서는 profile 객체입니다.
  • index: 인덱서 프로퍼티에 사용되는 인덱스 값입니다. 이 코드에서는 인덱서 프로퍼티를 사용하지 않으므로 null을 전달합니다.

따라서 nameProperty.GetValue(profile, null)profile 객체의 Name 프로퍼티 값을 가져오고, phoneProperty.GetValue(profile, null)profile 객체의 Phone 프로퍼티 값을 가져옵니다.

Console.WriteLine("{0}, {1}", ..., ...);는 서식 문자열을 사용하여 두 개의 값을 출력합니다. {0}은 첫 번째 인자, {1}은 두 번째 인자로 대체됩니다.

따라서 이 코드는 profile 객체의 Name 프로퍼티 값과 Phone 프로퍼티 값을 가져와서 "{0}, {1}" 형식의 문자열로 출력합니다.


object profile = Activator.CreateInstance(type, "박상현", "512-1234");

object profile = Activator.CreateInstance(type, "박상현", "512-1234"); 이 부분은 리플렉션을 사용하여 Profile 클래스의 객체를 동적으로 생성하는 코드입니다.

Activator.CreateInstance(type, "박상현", "512-1234")type 변수에 저장된 타입 정보를 사용하여 객체를 생성합니다. 이때 "박상현"과 "512-1234"는 생성자의 매개변수로 전달됩니다. 즉, Profile 클래스의 public Profile(string name, string phone) 생성자가 호출되어 name 필드는 "박상현"으로, phone 필드는 "512-1234"로 초기화됩니다.

생성된 객체는 object 타입의 profile 변수에 할당됩니다. object 타입은 모든 .NET 클래스의 기본 클래스이므로, 어떤 타입의 객체든 object 타입 변수에 저장할 수 있습니다.

리플렉션이 이용된 부분

이 코드에서 리플렉션이 이용된 부분은 다음과 같습니다.

  • Type type = Type.GetType("DynamicInstance.Profile");: Profile 클래스의 타입 정보를 가져옵니다.
  • MethodInfo methodInfo = type.GetMethod("Print");: Print 메서드의 정보를 가져옵니다.
  • PropertyInfo nameProperty = type.GetProperty("Name");: Name 프로퍼티의 정보를 가져옵니다.
  • PropertyInfo phoneProperty = type.GetProperty("Phone");: Phone 프로퍼티의 정보를 가져옵니다.
  • Activator.CreateInstance(type, "박상현", "512-1234");: Profile 클래스의 객체를 생성합니다.
  • methodInfo.Invoke(profile, null);: Print 메서드를 호출합니다.
  • nameProperty.SetValue(profile, "박찬호", null);: Name 프로퍼티 값을 설정합니다.
  • phoneProperty.SetValue(profile, "997-5511", null);: Phone 프로퍼티 값을 설정합니다.
  • nameProperty.GetValue(profile, null), phoneProperty.GetValue(profile, null): Name 프로퍼티와 Phone 프로퍼티 값을 가져옵니다.

리플렉션을 이용하여 객체를 생성하면 좋은 점

  • 컴파일 시점에 타입 정보를 알 수 없는 경우에도 객체를 생성할 수 있습니다.
  • 문자열로 클래스 이름을 지정하여 객체를 생성할 수 있습니다.
  • 동적으로 메서드를 호출하거나 프로퍼티 값을 설정할 수 있습니다.

리플렉션은 매우 강력한 기능이지만, 성능이 좋지 않고 코드를 읽기 어렵게 만들 수 있으므로 주의해서 사용해야 합니다.

type 변수에 저장된 타입 정보를 사용하여 객체를 생성합니다

Type 변수는 특정 클래스의 정보를 담고 있는 변수입니다. 마치 설계도가 담긴 USB라고 생각하면 돼.

Activator.CreateInstance(type)은 이 USB에 담긴 설계도를 읽어서 실제 객체를 생성하는 메서드야.

즉, type 변수에 어떤 클래스의 정보가 담겨 있느냐에 따라 생성되는 객체가 달라지는 거지.

예를 들어, type 변수에 Profile 클래스의 정보가 담겨 있다면 Activator.CreateInstance(type)을 실행하면 Profile 클래스의 객체가 생성됩니다.

이처럼 Type 변수를 사용하면 컴파일 시점에 어떤 클래스의 객체를 생성할지 결정하지 않고, 런타임에 동적으로 객체를 생성할 수 있습니다.

0개의 댓글