6장 - 리플렉션1
2014. 12. 21. 22:58ㆍIT Books/시작하세요 C# 프로그래밍
어셈블리 파일에는 메타데이터가 있음.
BCL에서 제공하는 리플렉션 (Reflection) 관련 클래스를 이용하면 메타데이터 정보를 얻을 수 있다.
--
닷넷에서는 프로세스 구조가 다음과 같다.
Exe 프로세스 = 1개의 공유 AppDomain + 1개의 기본 AppDomain
-> 기본은 1개지만 임의의 수를 만들 수 있다.
AppDomain이 만들어지면 그 내부에 어셈블리들이 로드되는데,
리플렉션을 이용하면 현재 AppDomain의 이름과 그 안에 로드된 어셈블리 목록을 구할 수 있다.
1 2 3 4 5 6 7 | AppDomain currDomain = AppDomain.CurrentDomain; Console.WriteLine(currDomain.FriendlyName); foreach (var item in currDomain.GetAssemblies()) { Console.WriteLine(" " + item.FullName); } | cs |
이렇게 구한 어셈블리는 모듈의 집합이다.
이런 어셈블리 내부 정보는 계층적으로 접근 할 수 있다.
어셈블리 > n개의 모듈 > n개의 타입 (클래스) > n개의 메서드, 필드, 프로퍼티, 이벤트
1 2 3 4 5 6 7 8 9 10 | foreach (var item in currDomain.GetAssemblies()) { Console.WriteLine(" " + item.FullName); foreach (var module in item.GetModules()) { Console.WriteLine(" " + module.FullyQualifiedName); } } | cs |
멤버를 유형별로 나눠서 볼 수도 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | foreach (var asm in currDomain.GetAssemblies()) { Console.WriteLine(" " + asm.FullName); foreach (Type type in asm.GetTypes()) { Console.WriteLine(type.FullName); // 클래스 생성자 foreach (ConstructorInfo ctorInfo in type.GetConstructors()) { Console.WriteLine(ctorInfo.Name); } // 클래스 이벤트 foreach (EventInfo evInfo in type.GetEvents()) { Console.WriteLine(evInfo.Name); } // 클래스 필드 foreach (FieldInfo fInfo in type.GetFields()) { Console.WriteLine(fInfo.Name); } // 클래스 메서드 foreach (MethodInfo mInfo in type.GetMethods()) { Console.WriteLine(mInfo.Name); } // 클래스 프로퍼티 foreach (PropertyInfo pInfo in type.GetProperties()) { Console.WriteLine(pInfo.Name); } } } | cs |
멤버를 유형별로 나눠서 볼 수도 있다.
C#코드가 빌드되어 어셈블리에 포함되는 경우
이런 모든 정보를 조회할 수 있는 기술을 리플렉션이라 한다.
AppDomain, Assembly
EXE 프로세스 내에서 CLR에 의해 구현된 격리 공간.
원한다면 별도로 생성하는 것도 가능.
AppDomain newDomain = new AppDomain.CreateDomain("testDomain");
AppDomain currentDomain = AppDomain.CurrentDomain;
현재 쓰레드가 속한 실행 중인 어셈블리가 속한 AppDomain.
AppDomain에 Assembly로드하기!
1 2 3 4 5 6 7 8 9 10 11 | namespace ClassLibrary1 { public class Class1 { public Class1() { Console.WriteLine(typeof(Class1).FullName + " : Created!"); } } } | cs |
로딩 테스트를 위해 위와 같은 dll 프로젝트를 만들고
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | using System.Reflection; using System.Runtime.Remoting; namespace Reflection { class Program { static void Main(string[] args) { AppDomain appDomain = AppDomain.CreateDomain("MyAppDomain"); string dllPath = @"D:\Dropbox\work\csharp\ClassLibrary1\bin\Debug\ClassLibrary1.dll"; ObjectHandle objHandle = appDomain.CreateInstanceFrom(dllPath, "ClassLibrary1.Class1"); AppDomain.Unload(appDomain); } } } | cs |
잘 로딩된다.
C / C++ 과는 달리 로딩한 dll만 따로 해제를 할 수 없다.
닷넷의 경우 AppDomain을 해제하는 경우만 해당 AppDomain에 속한 어셈블리가 모두 해제된다.
다만 기본 AppDomain에 로딩된 어셈블리는 프로세스가 종료될 때까지 해제 불가.
**
C++과는 달리 닷넷에서는 AppDomain은 서로 격리되므로
클래스에 정의된 static필드도 동일한 클래스가 서로 다른 AppDomain에 로딩되었다면
각각의 AppDomain에 존재할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // 고전적인 방법 SystemInfo info = new SystemInfo(); info.WriteInfo(); // Type정보만으로 객체 생성. Activator 이용 Type systemInfo1 = Type.GetType("Reflection.SystemInfo"); object objInstance = Activator.CreateInstance(systemInfo1); // 기본 생성자를 호출함으로써 생성. Type systemInfo2 = Type.GetType("Reflection.SystemInfo"); ConstructorInfo ctr = systemInfo2.GetConstructor(Type.EmptyTypes); object objInst2 = ctr.Invoke(null); // 메서드 호출 MethodInfo methodInfo = systemInfo2.GetMethod("WriteInfo"); methodInfo.Invoke(objInst2, null); FieldInfo fInfo = systemInfo2.GetField("is64Bit", BindingFlags.NonPublic | BindingFlags.Instance); // 기존 값 가져오기 object oldValue = fInfo.GetValue(objInst2); // 새 값 쓰기 fInfo.SetValue(objInst2, !Environment.Is64BitOperatingSystem); // 확인을 위해 메서드 호출 methodInfo.Invoke(objInst2, null); | cs |
객체를 생성하고 메서드를 호출하는 여러 방법.
리플렉션은 코드에서 보다시피
OOP의 캡슐화도 무시한다.
'IT Books > 시작하세요 C# 프로그래밍' 카테고리의 다른 글
7장 - c#2.0 - 제네릭 (0) | 2014.12.25 |
---|---|
6장 - 리플렉션2 (확장모듈 만들어보기) (0) | 2014.12.24 |
6장 - 쓰레드, 네트워크, 데이터베이스 (0) | 2014.12.21 |
6장 - 3 (컬렉션) (0) | 2014.12.20 |
6장 - BCL - 2 (0) | 2014.12.08 |