2. 리플렉션의 초석: System.Type 클래스
System.Type 클래스는 프레임워크 상에 존재하는 모든 종류의 개체들의 형식에 관한 정보를 대변하는 도우미 클래스입니다. 형식을 나타내는 요소로는, 그 자신이 가지고 있는 멤버 메서드들, 멤버 프로퍼티들, 멤버 필드들, 멤버 이벤트들 모두가 포함됩니다. 그리고 클래스 자신의 시그니처 (이름과 위치하는 네임스페이스)와 부모 클래스, 구현하는 인터페이스들, 특유의 속성들도 형식에 포함되는 것입니다.
위에서 언급한 모든 정보들은 메타 데이터라는 것으로 표현됩니다. 메타 데이터를 기초로 하여 각종 형변환과 리플렉션과 관련된 작업들을 처리하는 것으로 보면 맞습니다. 사실 역 어셈블리가 가능하여 코드 자체의 보안이 (런타임 상의 보안이 아니라) 매우 취약하다고 하는 것도 여기에서 발단이 된 것입니다. 네이티브 런타임에서는 RTTI (Run-Time Type Information) 라는 것으로 비슷한 기능을 제공하기도 하지만 닷넷의 리플렉션에는 훨씬 못미치는 수준의 기능입니다.
지금 설명하려는 System.Type 클래스는 위에 나열된 모든 정보를 자유자재로 탐색할 수 있도록 도와주는 클래스입니다. 하지만 System.Type 클래스 자체의 생성자를 직접 호출할 수는 없게 되어있습니다. 바로 추상 클래스이기 떄문인데, 형식을 조회하려면 생성자가 아닌 방법을 사용해야 합니다.
2.1. 리플렉션을 시작하기 전에 알아두어야 할 것
조사하고자 하는 클래스를 컴파일러가 컴파일을 시도할 당시에 알고 있는가, 모르고 있는가에 따라서 Type 클래스를 생성하는 방법이 크게 달라집니다. 즉, 컴파일러에 -r 옵션을 사용하여 어떤 어셈블리를 지정하였거나 코드 파일 중에 클래스가 정의되어있었다면 흔히 사용하던 언어 구문을 사용할 수 있습니다. 하지만 컴파일러에 일체의 지시를 내린적이 없었던 상태에서 어떤 어셈블리 파일을 직접 열어서 활용할 경우에는 언어 구문이 아닌 System.Reflection의 API들을 활용하지 않으면 안됩니다.
하지만 예외 사항은 있습니다. Visual Basic은 언어의 이미지와 특성 상 "편리성"에 초점을 두고 있다보니 이런 점에 있어서도 자동화를 시도합니다. 따라서, 컴파일러에 지정한 적이 없던 정보도 동적으로 분석하여 언어 구문으로 리플렉션을 사용할 수 있도록 해줍니다.
그리고 강의 중에 나오는 용어로 어셈블리라는 것이 있는데, 혼동하지 마십시오. 이것은 MASM 등에서 사용하는 어셈블리 언어를 의미하는 것이 아닙니다. 어셈블리는 프레임워크에서 다수의 네임스페이스와 다수의 개체를 묶어두는 단위로 이해하시면 되겠습니다.
2.2. typeof() 연산자
typeof() 연산자는 Type 클래스를 컴파일러가 이미 알고 있는 클래스로부터 추출할 수 있도록 도와주는 연산자입니다. 사용 방법은 무척 간단합니다. typeof() 연산자의 괄호 안에는 흔히 생각할 수 있는 형식의 이름을 지정하면 됩니다. (예: System.String, System.Object, MyNameSpace.MyClass, ...)
Type myType = typeof(System.String);
Console.WriteLine(myType.ToString());
2.3. GetType() 메서드
GetType() 메서드는 typeof() 연산자로는 찾을 수 없는 개체의 형식 정보를 조회할 수 있도록 도와주는 메서드입니다. System.Object (프레임워크 상의 최상위 부모 클래스)에 정의된 상태로 하위 클래스가 구현하는 형태로 되어있으므로 NULL 참조가 아닌 모든 개체가 가지고 있는 메서드입니다. 혹, 직접 구현한 적이 없었다 할지라도 프레임워크가 자동으로 현재 클래스에 대한 메타데이터를 대변하는 Type 클래스를 반환합니다.
public class ExposedClass
{
public void ExposedMethod(object anyThing)
{
if(anyThing != null)
{
Type myType = anyThing.GetType();
Console.WriteLine(myType.ToString());
}
}
}
위의 2.3 섹션의 코드를 활용해 보시고 싶으시다면 두 개의 어셈블리를 만드셔야 합니다. 하나는 클래스 라이브러리 (DLL)로 위의 코드를 포함하는 파일이어야 하고, 또 하나는 실행 파일로 해당 클래스 라이브러리를 링크하고 있고 다음의 코드가 포함되어야 합니다.
public class MyClass
{
public int myType = 1;
}
public class MainClass
{
[STAThread()]
public static void Main(string[] arguments)
{
ExposedClass.ExposedMethod(new MyClass());
}
}
참고: ExposedMethod 자체는 static이어도 문제는 없습니다. 어떤 Object를 인수로 받을 요량으로 만들어진것이니까요. 하지만 저 ExposedMethod가 static이 되게 된다면 "this" 키워드는 사용을 못하겠지요. 이런 차이점이 있습니다.