이번 장에서는 컴파일 타임에 전혀 알지 못했던 타입에 대해 타입의 세부 정보를 알아내고 인스턴스를 생성하는 등의 방법(리플렉션)에 대한 것을 배운다. 이는 주로 동적으로 확장 가능한 프로그램을 만들 때 활용되는 기법이다.

동적으로 확장 가능한 프로그램은 22장에서 살펴본 CLR 호스팅과 앱도메인의 장점을 활용할 수 있다. 호스트는 고유의 앱도메인 내에서 다른 앱도메인의 코드(애드인 등)를 수행할 수 있고 또 언로드할 수 있다.

어셈블리 로딩

컴파일 타임에 JIT 컴파일러가 IL을 컴파일하다 보면 어떤 타입들이 참조되고 있는지를 알게 된다. 그리고 런타임에는 참조되고 있는 타입들이 어느 어셈블리에 정의되어 있는지를 확인하기 위해서 TypeRef, AssemblyRef 메타데이터 테이블을 이용한다.

AssemblyRef 테이블에는 어셈블리의 이름을 구성하기 위해 필요한 모든 조건들을 가지고 있다. JIT 컴파일러는 이름, 버전, 문화권, 공용 키 토큰을 모두 모아 문자열로 결합한 후, 어셈블리 구분자를 만들고 이를 이용하여 어셈블리 앱도메인으로 읽어오려 한다.

내부적으로 CLR은 System.Reflection.Assembly 클래스의 static Load 메서드를 이용해서 이를 로드한다. (Win32의 LoadLibrary의 CLR 버전)

public class Assembly {
	public static Assembly Load(AssemblyName assemblyRef);
	public static Assembly Load(string assemblyString);
	// ...
}

CLR은 Load가 호출되면 정책에 따라 해당 어셈블리를 찾아나간다.

어셈블리를 찾아냈다면, Assembly 객체를 생성하고 그 참조를 반환한다. 만약 Load 메서드가 어셈블리를 찾지 못하면, System.IO.FileNotFoundException을 발생시킨다.

동적으로 확장 가능한 대부분의 응용프로그램이 Assembly.Load를 이용하여 특정 앱도메인으로 어셈블리를 로드한다.

만약 사용자로부터 특정 어셈블리의 경로명을 받아 로드하려면 Assembly.LoadFrom 메서드를 호출할 수 있다.

public class Assembly
{
	public static Assembly LoadFrom(string path);
}

AppDomain.Load 메서드는 어셈블리의 참조를 반환한다. Assembly 클래스는 System.MarshalByRefObject를 상속하기 않았기 때문에, 호출한 앱도메인 쪽으로 객체를 반환하기 위해선 반드시 값으로 마샬링되어야 한다.

내부적으로는 LoadFrom은 System.Reflection.AssemblyName.GetAssemblyName을 호출한다.

해당 메서드 내부에서는 지정한 파일을 열고 AssemblyDef 메타데이터 테이블의 항목을 찾아서 어셈블리 구분자 정보를 획득한 후, AssemblyName 객체로 반환해준다.

이제 LoadFrom은 내부적으로 위의 Assembly.Load를 호출하는데, 이 때 반환 된 AssemblyName을 매개 변수로 전달한다.