안녕하세요 삽잡이입니다.
이번 시간부터는 실시간에 '몰랐던' 타입을 로딩하고,
그 로딩한 타입의 인스턴스(객체)를 생성하는 등의 작업에 대해서 알아보려고합니다.
차근차근 알아보도록 하겠습니다.
이전에 배웠던 내용들을 한번 생각해볼까요?
우선, 어셈블리는
배포되고 버전관리가 되는 단위라고 말했었습니다.
그리고, 메타데이터는
정보의 정보 즉, '함축적인 정보'라고 헀습니다.
정보를 알게 되면 더 많은 정보들을 뽑아낼 수 있다는 의미입니다.
대표적인 메타데이터의 예로, 벡터 이미지가 있지요.
벡터 이미지는 픽셀이라는
위치값에 따르는 색상값 즉, 사람들이 인식할 수 있는 데이터들의 집합입니다.
벡터 이미지는 이미지이자만, 정보를 가지고 있는 이미지입니다.
이미지를 나타내는 정보 말입니다.
이처럼 함축적인 정보를 바로 '메타'라고 부릅니다.
그러면 왜 이 정보들을 함축적인 정보, 정보의 정보라고 했을까요?
바로 해당 정보를 통해 이미지를 만들 수 있기 때문입니다.
아무튼... 데이터에 대한 정보의 정보를 그래서 메타 데이터라고 부릅니다.
그런데 이 메타데이터가 어셈블리에는 두개나 있습니다.
우선, 어셈블리 메타데이터가 있습니다.
해당 메타 데이터는 굉장히 중요한 메타데이터이기 때문에
'매니 페스트'라는 이름으로 용어가 굳어진 것입니다.
무엇인가 어셈블리를 이루는 전체에 대한 모든 정보를
해당 장소에 담는다고 생각하시면 됩니다.
전체에 대한 모든 정보가 매니페스트라고 할 수 있지요.
이전에 보여드렸지만, IL DASM과 같은 도구를 통해 확인해보시면
함축 정보를 다 가지고 있다는 것을 알 수 있습니다.
그리고 형식 메타데이터가 있습니다,.
CIL 코드에서 사용하는 모든 정보를 가지고 있지요.
예를 들어 CIL에 A, B, C를 사용하게 되면
해당 A, B, C에 대한 모든 정보가 다 형식 메타 데이터에 들어가게됩니다.
심지어 외부 어셈블리에서 참조하더라도 해당 메타 데이터에 들어가있습니다.
참고로, 내가 만든 녀석은 def라고 앞에 붙어있고,
외부에서 가져다 쓴 녀석은 ref라고 붙어있습니다.
우리가 앞으로 배우려는 것은 바로 '리플렉션', '늦은 바인딩', '동적 로딩'입니다.
그런데, 이 모든 기능들은 '형식 메타 데이터'때문에 가능한 기능들입니다.
형식 메타 데이터가 핵심이라는 소리입니다.
그래서 이렇게 주구장창 설명만 하고 있었지요...
닷넷에서 중요한 요소중 하나를 뽑자고 하면
타입에 대한 모든 정보는 모두 메타데이터에 저장된다는 것입니다.
메타데이터를 보게되면 생성자, 필드, 속성, 인터페이스, 메서드 정보등의 다 들어있습니다.
아무튼... CIL 곧, 어셈블리 코드에 사용되는 모든 내용이
메타데이터에 담긴다는 사실을 기억하시길 바랍니다.
자... 그래서 도대체 리플렉션이 뭡니까 ^^... |
닷넷에서 리플렉션이란,
'런 타임'시 타입의 정보를 찾는 과정을 말합니다.
여기서 런타임이라는 것이 중요합니다. 컴파일 때가 아닙니다!
이 리플렉션에 참여하는 타입들은
Assembly, EventInfo, FieldInfo, MethodInfo등이 있습니다.
대충 봐도 모두 다 어셈블리 타입과 관련된 내용이라는 사실을 알 수 있습니다.
그런데 여러분 객체를 만들 떄 어떻게 만들어집니까?
C++까지는 클래스의 이름을 보고 객체를 만들었습니다.
즉, 컴파일러가 그렇게 해줬습니다.
하지만, 닷넷은 아닙니다. 뭔소리냐구요?
C#에서는 Sytem.Type 클래스를 제공해줍니다.
해당 클래스는 형식에 대한 모든 정보를 담을 수 있는 추상화 클래스입니다.
닷넷은 정확하게 클래스를 보고 객체가 만들어지는 것이 아니라,
클래스를 보고 타입 객체가 만들어집니다.
그리고 타입 객체를 보고 실제 객체(인스턴스)가 만들어지는 것입니다.
즉, Type이라는 형식의 객체를 만들어내는데,
해당 객체가 바로 클래스에 대한 객체가 되는 것입니다.
정리하자면, 우선 내부적으로 클래스를 보고
CLR에서 요구한 대로 타입 클래스 객체가 만들어지는 것입니다.
타입 객체는 딱! 한번 만들어집니다.
타입에 대한 설명을 해주는 객체는 단 하나만 있어도 충분하지요.
왜 이런 개념을 만들었을까요?
CIL 코드 내에 엄청나게 많은 타입들이 있다고 가정해봅시다.
뭐 그 형식들을 다 사용하면 상관없겠지만,
사용하지도 않는 형식들을 다 알 필요가 있을까요?
따라서 실행할때마다 필요한 형식들만 사용하게 되는것입니다.
실제로 사용하는 타입만이 타입 객체가 만들어지는 것이지요.
이러한 개념이 있기 때문에,
닷넷에서는 클래스 없이도 객체를 만들 수 있게 됩니다.
도대체 뭔 x소리야 ^^... |
그런데 이와 같은 이유 덕분에 리플렉션인 가능한 것입니다.
리플렉션의 정의에 대해서 다시 한번 위로 올라가서 보시고 잘 생각해보시길 바랍니다.
닷넷에서는 객체를 만들기 위해서는 타입 객체만 있으면 만들 수 있습니다.
어셈블리에 해당 클래스가 없더라도,
뭔진 모르지만, 타입 객체만 있으면 객체를 만들어낼 수 있다 이겁니다.
따라서 굉장히 중요한 형식이 Type 형식입니다.
타입에 대한 완벽하고도 모든 정보가 들어있는 추상화 클래스입니다.
Type 클래스에 있는 이 내용물들의 다수는
모두 다 리플렉션에서 받는 형식들을 반환하거나 필요로 합니다.
예를 들어서 Point Class가 있다고 해봅시다.
Point 클래스 타입 객체를 하나 만들어내는데,
이는 CLR(런타임)이 만들어줍니다.
그러면 이 Type 객체는 일반적으로
해당 타입이 인식될 때 만들어집니다.
이 타입 객체가 만들어지게 되면
최초로 호출되는 메서드 하나가 바로 '정적 생성자'입니다.
이 정적 생성자가 호출되는 이유는 정적 필드를 초기화 하기 위해서입니다.
아무튼, 타입이 읽혀질 때, 인식될 때라고 하지 않았습니까?
이때! 타입 객체가 만들어지는 것입니다.
어찌됬건,
타입들이 존재하면 객체를 만들 수 있어야하지 않겠습니까?
따라서, new 를 통해 객체를 만들기 전에
이미 타입을 설명하는 객체들이 만들어진다 이겁니다.
그러면 결론적으로 Point에 대한 클래스 정보는
형식 메타 데이터에 들어가는 것이고
이 형식 메타 데이터의 정보를 보고 타입 객체가 만들어지는 것입니다.
아무튼! 우리의 객체는 타입 객체를 통해 만들어지는 것이라는 것이죠.
(아무튼이라는 말을 많이쓰군요.. 허허...)
위에서 리플렉션에 참여하는 멤버의 일부들을 명시할 때
모두 ~Info라는 접미사가 붙지 않았습니까?
모두 Type 형식의 내용물 즉, 타입의 인터페이스를 나타내는 것입니다.
자... 그러면 이제 계속 이어가봅시다.
우선, 타입 객체를 어떻게 만들어내거나 얻을 수 있을까요?
여기에는 세가지 방법이 있습니다.
첫번째로, object의 GetType() 메서드,
두번째로, typeof 연산자
그리고 마지막으로, Type 클래스에 있는 GetType()이라는 정적 메서드입니다.
해당 메서드는 Type의 이름의 문자열로 타입 객체를 얻는 메서드입니다.
그러면 object 의 메서드가 아니라
두,세번쨰 방식으로 타입 객체를 받을 때의 차이점을 생각해보겠습니다.
우선 두 가지의 방식을 모두 사용하더라도 반환 되는 것은 '타입 객체'입니다.
그런데 이 타입 객체의 성격이 좀 다릅니다.
좌측은 기존에 있는 형식을 인자로,
우측은 정적 메서드를 호출하는 것인데, '문자열'을 줘야합니다.
좌측은 Point라는 타입을 해당 어셈블리가 가지고 있어야 타입 객체를 얻을 수 있게 됩니다.
즉, Point라는 타입을 어셈블리가 알고 있을 때라는 것입니다.
그런데, 우측은 타입이 아닙니다. '문자열'입니다.
즉, Point라는 타입을 가지고 있지 않더라도 타입 객체를 얻을 수 있다는 소리입니다.
또한, 좌측은 사실 어떤 형식이 있으면
해당 형식의 GetType() 메서드를 통해 타입 객체를 얻을 수도 있습니다.
(사실 CLR로 부터 만들어진 타입 객체를 얻는 것입니다. 만드는 것이 아닙니다!)
그런데, 후자는 Point에 대한 타입 정보가 어디에도 없습니다.
즉, 컴파일 시에는 몰라도 잘 된다 이겁니다.
실행 시간에만 정말 Point라는 형식이 있도록 만들어주면 된다 이겁니다.
이게 가장 큰 차이점입니다.
Type의 GetType 메서드를 보면 세가지의 인자가 올 수 있습니다.
즉, 기존에 문자열과 두 개의 bool 형식 매개변수를 받는
오버로드 된 메서드가 있다 이겁니다.
실제 이름은 네임스페이스를 포함된 클래스명이 필요하다는 사실은 다들 아실 것입니다.
두번째 매개변수는 예외 발생 여부를 설정할 수 있습니다.
그리고 세번째는 대소문자를 구분할지에 대한 여부를 설정할 수 있습니다.
보통 true를 쓰는것이 default 입니다.
아무튼..
이 리플렉션 즉, 타입의 정보를 런타임 시간에 가져오는 과정은,
메서드, 필드, 속성, 인터페이스 등 예외가 없습니다.
메서드에 대한 리플렉션을 볼까요?
Tpye 형식에 대한 t의 GetMethod()는 '메서드의 목록을' 반환해줍니다.
즉, t라는 타입의 메서드 목록을 배열로 반환해준다 이겁니다.
그러면 메서드 형식을 보관할 형식이 필요하겠지요.
그게 바로 MethodInfo[] 입니다.
Method는 리플렉션 네임스페이스의 일부 멤버였습니다.
매개변수 파라메터 리플렉션은 '메서드'를 통해서 할 수 있습니다.
즉, MethodInfo 형식에서 객체 하나를 얻어
GetParameters() 메서드를 통해 ParameterInfo 형식을 반환할 수 있다 이겁니다.
타입에게 메서드의 목록을 요구하듯이
매개변수로 받는 메서드의 내부에 수 많은 타입들이 있을 것이기에
메서드에게 매개변수 리플렉션을 요구하는 것입니다.
즉, 매개변수를 메서드에게 요구한다는 것입니다.
우리는 타입 클래스에 대해서 공부한 것입니다.
이 타입 클래스에는 타입에 모든 정보가 들어있는 것이고,
이 안에는 GetMethodInfos()와 같은 메서드들이 존재합니다.
해당 메서드 안에는 메서드에 대한 정보가 들어가있고,
메서드의 정보는 MethodInfo 형식으로 표현되는 것입니다.
~Info로 표현이 되는 것이니 배열 즉, 목록을 반환하는 것입니다.
Type에는 필드, 인터페이스, 속성을 반환하는 메서드가 있는 것을 확인했었습니다.
해당 함수들은 모두 Type을 가지고 있죠.
그런데, 매개변수의 정보도 Type이 가지고 있겠습니까?
메서드가 가지고 있겠지요. 따라서, 메서드에게 물어봐야합니다.
한 마디로, 메서드에는 파라메터 목록을 가지고 있는
인터페이스를 가지고 있다 이겁니다.
Method의 정보는
Type 클래스 즉, 타입 객체에서 얻어오는 것이구요,
메서드를 읽어왔다면,
GetParameters() 메서드를 통해 파라메터의 목록을 반환받을 수 있다는 것입니다.
이번 시간은 여기까지 하도록 하겠습니다.
이상 삽잡이였습니다!
'삽질의 현장 > - .NET' 카테고리의 다른 글
#074_닷넷(.NET)_.Net Framework 기본 - 특성 (Attribute) (0) | 2015.11.11 |
---|---|
#073_닷넷(.NET)_.Net Framework 기본 - 동적 어셈블리 로딩 (0) | 2015.11.11 |
#071_닷넷(.NET)_.Net Framework 기본 - 어셈블리 (구성 파일) (0) | 2015.11.10 |
#070_닷넷(.NET)_.Net Framework 기본 - 어셈블리 (공유 어셈블리) (0) | 2015.11.10 |
#069_닷넷(.NET)_.Net Framework 기본 - 어셈블리(assembly) (1) | 2015.11.07 |