C#/리플렉션(Reflection)

[C#]리플렉션(Reflection)이란?

DevStory 2022. 9. 25.

리플렉션(Reflection)이란?

리플렉션이란 애플리케이션 실행 중 어셈블리의 내용을 확인하거나 검사하려는 경우 사용되는 기능입니다. 어셈블리의 내용의 종류는 메서드, 프로퍼티, 생성자 등 쉽게 이야기하자면, 인스턴스의 데이터 타입 정보입니다.

 

이번 포스팅은 리플렉션의 개념과 간단한 사용 방법을 소개합니다.


리플렉션을 사용해야 하는 경우

리플렉션은 주로 애플리케이션을 개발할 때, 디버깅 또는 런타임에 알 수 없는 객체의 동작을 분석하기 위해 사용하거나 외부 라이브러리에 존재하는 클래스 및 메서드를 분석하는 목적으로 사용됩니다.

 

즉, 리플렉션은 테스트 및 디버그 목적으로 사용하는 기능입니다. 리플렉션을 사용하는 경우 성능 오버헤드를 일으킬 수 있으므로 반드시 테스트 및 디버그 목적으로 사용하는 것이 좋으며, 애플리케이션 개발이 완료되었거나 테스트가 완료된 경우 리플렉션을 사용한 소스 코드는 주석 및 제거하는 것이 좋습니다.


Object 클래스의 GetType 메서드

C#의 Object 클래스는 모든 데이터 타입의 조상이며, 모든 데이터 타입은 Object에서 파생됩니다. 따라서, 모든 데이터 타입은 Object 클래스에 존재하는 ToString(), Equals(), GetType(), GetHashCode(), ReferenceEquals() 메서드를 사용할 수 있습니다.

 

다섯 개의 메서드 중에 GetType() 메서드는 객체의 데이터 타입을 반환하는 기능을 수행합니다. GetType() 메서드를 사용하여 해당 객체 또는 변수에 대해 데이터 타입을 확인할 수 있습니다.

 

GetType() 메서드는 Type이라는 데이터 타입의 객체를 반환합니다. Type은 데이터 타입의 모든 정보를 담고 있는 클래스입니다.

 

다음 예제는 GetType() 메서드를 사용하여 데이터 타입 및 네임스페이스를 콘솔에 출력합니다.

class Program
{
  static void Main(string[] args)
  {
    int num = 10;
    Type type1 = num.GetType();
    Console.WriteLine(type1.Name);
    Console.WriteLine(type1.Namespace + "\n");

    bool boolValue = false;
    Type type2 = boolValue.GetType();
    Console.WriteLine(type2.Name);
    Console.WriteLine(type2.Namespace + "\n");

    string strValue = "Hello";
    Type type3 = strValue.GetType();
    Console.WriteLine(type3.Name);
    Console.WriteLine(type3.Namespace + "\n");

    ArrayList al = new ArrayList();
    Type type4 = al.GetType();
    Console.WriteLine(type4.Name);
    Console.WriteLine(type4.Namespace);
  }
}

[실행 결과]

Int32
System

Boolean
System

String
System

ArrayList
System.Collections

데이터 타입을 가져오는 또 다른 방법

위에서 GetType() 메서드 사용 방법을 간단하게 보여줬습니다. GetType() 메서드는 한 가지 단점이 존재하는데, 반드시 객체의 인스턴스가 존재해야 호출할 수 있습니다.

 

객체의 인스턴스를 생성하지 않고 데이터 타입을 가져오는 방법으로 typeof 연산자와 Type 클래스의 GetType() 메서드를 사용할 수 있습니다.

 

typeof 연산자는 데이터 타입을 매개변수로 전달하면 되고 Type 클래스의 GetType() 메서드에는 네임스페이스를 포함한 데이터 타입을 문자열로 전달합니다.

 

다음 예제는 Object 클래스의 GetType() 메서드, typeof 연산자, Type 클래스의 GetType() 메서드 사용 방법입니다.

class Program
{
  static void Main(string[] args)
  {
    // 1. Object 클래스의 GetType() 메서드
    ArrayList al = new ArrayList();
    Type type1 = al.GetType();
    Console.WriteLine(type1.Name);

    // 2. typeof 연산자
    Type type2 = typeof(ArrayList);
    Console.WriteLine(type2.Name);

    // 3 Type 클래스의 GetType() 메서드
    Type type3 = Type.GetType("System.Collections.ArrayList");
    Console.WriteLine(type3.Name);
  }
}

[실행 결과]

ArrayList
ArrayList
ArrayList

Type 클래스에서 제공하는 메서드

Type 클래스는 다양한 메서드를 제공하지만, 자주 사용되는 메서드만 정리하였습니다.

메서드 반환 타입 설명
GetConstructors() ConstructorInfo[] 해당 데이터 타입의 모든 생성자를 배열로 반환합니다.
GetFields() FieldInfo[] 해당 데이터 타입의 모든 필드를 배열로 반환합니다.
GetInterfaces() Type[] 해당 데이터 타입이 상속하는 모든 인터페이스를 배열로 반환합니다.
GetMembers() MemberInfo[] 해당 데이터 타입의 모든 멤버를 배열로 반환합니다.
GetMethods() MethodInfo[] 해당 데이터 타입의 모든 메서드를 배열로 반환합니다.
GetProperties() PropertyInfo[] 해당 데이터 타입의 모든 프로퍼티를 배열로 반환합니다.

이번 포스팅은 GetInterfaces() GetFields(), GetMethods() 메서드를 사용하는 방법을 예제를 통해 보여주며, 자세한 사용 방법 및 설명은 따로 포스팅할 예정입니다.

 

표에 작성된 메서드와 반환 타입을 사용하기 위해 다음 네임스페이스를 추가합니다.

using System.Reflection;

인터페이스 목록 확인

다음 예제는 int 타입이 상속하는 인터페이스의 이름을 출력합니다.

class Program
{
  static void Main(string[] args)
  {
    int num = 0;

    Type type = num.GetType();
    Type[] interfaces = type.GetInterfaces();

    foreach(Type i in interfaces)
    {
      Console.WriteLine("Name:{0}", i.Name);
    }
  }
}

[실행 결과]

Name:IComparable
Name:IConvertible
Name:IFormattable
Name:IComparable`1
Name:IEquatable`1
Name:ISpanFormattable

필드 목록 확인

다음 예제는 int 타입에 존재하는 필드의 타입과 이름을 출력합니다.

class Program
{
  static void Main(string[] args)
  {
    int num = 0;

    Type type = num.GetType();
    FieldInfo[] fieldInfos = type.GetFields();

    foreach(FieldInfo field in fieldInfos)
    {
      Console.WriteLine("Type:{0}, Name:{1}", 
        field.FieldType, field.Name);
    }
  }
}

[실행 결과]

Type:System.Int32, Name:MaxValue
Type:System.Int32, Name:MinValue

메서드 목록 확인

다음 예제는 int 타입의 메서드 이름과 반환 타입 그리고 메서드 파라미터의 정보를 출력합니다.

class Program
{
  static void Main(string[] args)
  {
    int num = 0;

    Type type = num.GetType();
    MethodInfo[] methodInfos = type.GetMethods();

    foreach(MethodInfo method in methodInfos)
    {
      Console.WriteLine("Method Return Type:{0}, Method Name:{1}",
          method.ReturnType, method.Name);

      ParameterInfo[] parameterInfos = method.GetParameters();
      foreach(ParameterInfo param in parameterInfos)
      {
        Console.WriteLine("Parameter Type:{0}, Parameter Name:{1}",
            param.ParameterType, param.Name);
      }
      Console.WriteLine();
    }
  }
}

[실행 결과]

Method Return Type:System.Int32, Method Name:CompareTo
Parameter Type:System.Object, Parameter Name:value

Method Return Type:System.Int32, Method Name:CompareTo
Parameter Type:System.Int32, Parameter Name:value

Method Return Type:System.Boolean, Method Name:Equals
Parameter Type:System.Object, Parameter Name:obj

Method Return Type:System.Boolean, Method Name:Equals
Parameter Type:System.Int32, Parameter Name:obj

Method Return Type:System.Int32, Method Name:GetHashCode

Method Return Type:System.String, Method Name:ToString

Method Return Type:System.String, Method Name:ToString
Parameter Type:System.String, Parameter Name:format

Method Return Type:System.String, Method Name:ToString
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.String, Method Name:ToString
Parameter Type:System.String, Parameter Name:format
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Boolean, Method Name:TryFormat
Parameter Type:System.Span`1[System.Char], Parameter Name:destination
Parameter Type:System.Int32&, Parameter Name:charsWritten
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:format
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.TypeCode, Method Name:GetTypeCode

Method Return Type:System.Type, Method Name:GetType

BindingFlags 열거형 활용

int 타입의 메서드와 파라미터를 출력하는 예제의 실행 결과에서 볼 수 있듯이 int 타입은 상당히 많은 메서드와 파라미터를 가지고 있습니다.

 

원하는 항목만 가져오고 싶은 경우 BindingFlags 열거형을 사용할 수 있습니다. BindingFlags 열거형을 사용하면, 정적 메서드만 가져오거나 public 또는 public이 아닌 정보만 가져올 수 있으며, 이외에도 다양한 조건을 설정할 수 있습니다.

 

다음 예제는 int 타입에서 접근 한정자가 public인 정적(Static) 메서드만 가져옵니다.

class Program
{
  static void Main(string[] args)
  {
    int num = 0;

    Type type = num.GetType();
    MethodInfo[] methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.Static);

    foreach(MethodInfo method in methodInfos)
    {
      Console.WriteLine("Method Return Type:{0}, Method Name:{1}",
          method.ReturnType, method.Name);

      ParameterInfo[] parameterInfos = method.GetParameters();
      foreach(ParameterInfo param in parameterInfos)
      {
        Console.WriteLine("Parameter Type:{0}, Parameter Name:{1}",
            param.ParameterType, param.Name);
      }
      Console.WriteLine();
    }
  }
}

[실행 결과]

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Int32, Method Name:Parse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.String, Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider
Parameter Type:System.Int32&, Parameter Name:result

Method Return Type:System.Boolean, Method Name:TryParse
Parameter Type:System.ReadOnlySpan`1[System.Char], Parameter Name:s
Parameter Type:System.Globalization.NumberStyles, Parameter Name:style
Parameter Type:System.IFormatProvider, Parameter Name:provider
Parameter Type:System.Int32&, Parameter Name:result

리플렉션의 단점

  • 성능 오버헤드(Performance Overhead): Reflection은 유용한 기능입니다. 하지만 런타임에 Reflection API를 사용하여 알 수 없는 객체를 처리하는 과정은 성능적으로 좋지 않습니다. 따라서 Reflection을 사용하지 않는 방향으로 개발해야 합니다.
  • 보안 제한(Security Restrictions): Reflection은 컴파일 타임이 아닌 런타임 기능이므로 런타임 권한이 필요할 수 있습니다. 따라서 보안 때문에 Reflection를 사용한 소스코드가 실행할 수 없는 경우 무용지물이 될 수 있습니다.
  • 내부 노출(Exposure of Internals): Reflection을 사용하여 클래스의 메서드와 필드를 접근할 수 있습니다. 코드를 이식하지 않아도 되는 장점은 객체 지향 프로그래밍의 특징인 추상화를 위반합니다.
반응형

댓글