- 앞장에서 보았듯이, C# 프로그램 소스 코드를 컴파일하면 어셈블리가 만들어진다. 어셈블리는 컴파일된 코드와 메타자료(metadata) 그리고 기타 자원들로 구성된다. 컴파일된 코드와 메타자료를 실행시점에서 조사하는 것을 가리켜 반영(reflection)이라고 한다.
- 어셈블리 안에 담긴 컴파일된 코드에는 원 소스 코드의 내용이 거의 다 들어 있다. 지역변수 이름이나 주석, 전처리기 지시문 등의 일부 정보는 컴파일 과정에서 사리지지만 그 나머지의 상당 부분은 반영 기능을 이용해서 접근할 수 있다.
- 실제로 반영 기능을 이용해서 역컴파일러(decompiler)를 작성하는 것도 가능하다.
- .NET Framework가 제공하는 그리고 C#을 통해서 노출되는 여러 서비스(동적 바인딩, 직렬화, 자료 바인딩, Remoting 등)는 메타자료가 있어야 작동한다.
- 독자가 작성하는 프로그램 역시 메타자료를 활용할 수 있으며 심지어는 커스텀 특성을 이용해서 메타자료에 새로운 정보를 추가할 수도 있다.
- 반영 API는 System.Reflection 이름공간에 들어 있다. System.Reflection.Emit 이름공간에 있는 클래스들을 이용하면 새 메타자료와 실행 가능한 IL(Intermediate Language; 중간 언어) 코드를 동적으로 생성하는 것도 가능하다.
- 이번 장에서 뭔가를 ‘동적으로’ 수행한다는 것은 형식 안전성이 오직 실행시점에서만 강제되는 어떤 작업을 반영 긴으을 이용해서 수행하는 것을 뜻한다.
- 구체적인 메커니즘과 기능성은 다르지만 원칙적으로 이는 C#의 dynamic 키워드를 통한 동적 바인딩과 비슷하다.
- 둘을 비교하자면 동적 바인딩이 반영 기능보다 훨씬 사용하기 쉽다. 그리고 동적 바인딩은 동적 언어 상호운용성을 위해 DLR(Dynamic Language Runtime)을 활용한다.
- 반영은 사용하기가 비교적 번거롭고 오직 CLR만 고려한다. 그러나 CLR로 할 수 있는 것의 관점에서 본다면 좀 더 유연하다.
- 예컨대 반영 기능을 이용해서 형식들과 멤버들의 목록을 얻을 수 있고, 형식의 이름을 문자열로 지정해서 그 인스턴스를 생성할 수 있으며, 즉석에서 어셈블리를 구축할 수 있다.
형식의 반영과 활성화
Type 객체얻기
- System.Type의 인스턴스는 형식의 메타자료를 대표한다. Type은 널리 쓰이는 형식이라서 System.Reflection 이름공간이 아니라 System 이름공간에 들어 있다.
- System.Type의 인스턴스를 얻는 방법은 크게 두 가지이다. 하나는 임의의 객체에 대해 GetType을 호출하는 것이고 또 하나는 C#의 typeof 연산자를 사용하는 것이다.
Type t1 = DateTime.Now.GetType(); // 실행시점에서 얻은 Type 객체
Type t2 = typeof(DateTime); // 컴파일시점에서 얻은 Type 객체
- typeof 연산자는 배열 형식과 제네릭 형식도 지원한다.
Type t3 = typeof(DateTime[]); // 1차원 Array 형식
Type t4 = typeof(DateTime[,]); // 2차원 Array 형식
Type t5 = typeof(Dictionary<int, int>); // 닫힌 제네릭 형식
Type t6 = typeof(Dictionary<,>); // 묶이지 않은 제네릭 형식
- 또한 문자열 이름으로 Type 객체를 얻을 수도 있다. 형식이 속한 어셈블리에 대한 Assembly 객체가 있다면, 형식 이름으로 Assembly.GetType을 호출하면 된다.
Type t = Assembly.GetExecutingAssembly().GetType("Demos.TestProgram");
- Assembly 객체가 없는 경우에는 형식의 어셈블리 한정 이름을 통해서 그 형식을 대표하는 Type 객체(이하 간단히 형식 객체)를 얻을 수 있다.
- 어셈블리 한정 이름은 형식의 완전 한정 이름 다음에 어셈블리의 완전 한정 이름을 붙인 것이다.
- 이때 암묵적으로 해당 어셈블리가 Assembly.Load(string)을 호출했을 떄와 마찬가지 방식으로 적재된다.
Type t = Type.GetType("System.Int32, mscorlib, Version=2.0.0.0, " + "Culture=neutral, PublicKeyToken=b77a5c5...");
- System.Type 객체를 얻었다면 여러 속성을 통해서 형식의 이름, 어셈블리, 기반형식, 가시성 등에 접근할 수 있다. 예컨대 다음과 같다.
Type stringType = typeof(string);
string name = stringType.Name; // String
Type baseType = stringType.BaseType; // typeof(Object)
Assembly assem = stringType.Assembly; // mscorlib.dll
bool isPublic = stringType.IsPublic; // true
- System.Type 인스턴스는 형식의 전체 메타자료로 가는 관문이라 할 수 있다. 또한 형식이 정의되어 있는 어셈블리로의 접근 통로이기도 하다.
- System.Type은 추상 클래스이므로, 형식 객체에 대한 typeof 연산자는 Type의 특정 파생 클래스의 인스턴스를 돌려준다. CLR은 RuntimeType이라는 Type의 파생 클래스를 사용하는데, 이 파생 클래스는 mscorlib의 내부 클래스이다.
TypeInfo와 Windows 스토어 앱