Post List

2018/01/26

C#__6.0 리플렉션

리플렉션 대해 알아보겠습니다리플렉션은 객체를 X-Ray사진처럼 객체의 형식 정보를 들여다보는 기능입니다. 기능을 이용하면 프로그램 실행 중에 객체의 형식 이름부터, 프로퍼티 목록, 메소드 목록, 필드, 이벤트 목록까지 모두 열어볼 있습니다.
형식의 이름만 있다면 동적으로 인스턴스를 만들 수도 있고, 인스턴스의 메소드를 호출할 수도 있습니다. 심지어  새로운 데이터 형식을 동적으로 만들 수도 있습니다.
.NET 모든 형식을 들여다 있도록 장치를 설계했습니다. 바로 모든 데이터 형식의 조상인 Object형식에 GetType()메소드를 만들어 놓은 것이지요.

Object.GetType()메소드와 Type 클래스
Object 모든 데이터 형식의 조상입니다. 말은 , 모든 데이터 형식은 Object 형식이 갖고 있는 다음의 메소드를 물려받아 갖고 있다는 뜻이기도 합니다.

Equals()
GetHashCode()
GetType()
ReferenceEquals()
ToString()

다섯 메소드 중에 GetType() 보면, 메소드는 객체의 형식 정보를 반환하는 기능을 합니다. 모든 데이터 형식이 GetType() 메소드를 갖고 있기 때문에 어떤 객체에 대해서도 메소드를 호출해서 객체의 형식 정보를 얻어낼 있습니다.

형식 이름, 소속되어 있는 어셈블리 이름, 프로퍼티 목록, 메소드 목록, 필드 목록, 이벤트 목록 심지어는 형식이 상속하고 있는 인터페이스의 목록까지도 있습니다.

Object.GetType()메소드와 Type형식을 사용하는 방법에 대해 알아보겠습니다.

1
2
3
4
int a = 0;
Type        type   = a.GetType();
FieldInfo[] fields = type.GetFields(); 
cs


Object.GetType() 메소드 외에도 형식 정보를 얻는 방법이 있습니다.
Typeof 연산자와 Type.GetType()메소드 입니다.

Typeof 연산자와 Type.GetType() 메소드는 똑같이 Type형식을 반환하지만 ,typeof 연산자는 형식의 식별자 자체를 매개변수로 받고,
Type.GetType()메소드는 형식의 전체 이름, 네임스페이스를 포함한 형식 이름을 매개 변수로 받는다는 점이 다릅니다.

Typeof 연산자 사용을 아래와 같습니다.
1
Type a = typeof(int);
cs

Type.GetType() 사용 코드도 한번 보겠습니다.
1
Type b = Type.GetType("System.Int32");
cs



이번에는 리플렉션을 이용해서 특정 형식의 인스턴스를 만들고, 데이터를 할당하며, 메소드를 호출하는 방법을 설명하려 합니다. 이렇게 코드 안에서
런타임에 특정 형식의 인스턴스를 만들 있게 되면 우리는 조금 프로그램이 동적으로 동작할  있도록 구성할 있습니다.

리플렉션을 이용해서 동적으로 인스턴스를 만들기 위해서는 System.Activator 클래스의 도움이 필요합니다. 인스턴스를 만들고자 하는 형식의
Type객체를 매개 변수로 넘기면, Activator.CreateInstance() 메소드는 입력받은 형식의 인스턴스를 생성하여 반환합니다.

1
object a = Activator.CreateInstance(typeof(int));
cs

또한, 일반화를 지원하는 버전의 CreateInstance() 메소드도 있습니다. 가령 List<int> 인스턴스를 만들고 싶다면 아래와 같이 사용하면 됩니다.

1
List<int> list = Activator.CreateInstance<list<int>>();
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
class Profile
{
    public string Name { get; set; }
    public int Height { get; set; }
}
static void Main()
{
    Type type = typeof(Profile);
    Object profile = Activator.CreateInstance(type);
    // 여기서 Type.GetProperties() 메소드는 해당 형식의 모든 프로퍼티를 PropertyInfo 형식의 배열로 반환합니다.
    // 하지만 Type.GetProperty()메소드는 특정 이름의 프로퍼티만 찾아 반환합니다.
    PropertyInfo name   = type.GetProperty("Name");
    PropertyInfo height = type.GetProperty("Height");
    name.SetValue(profile, "정우성"null);
    height.SetValue(profile, 186null);
    string strName = (string)name.GetValue(profi
    int iHeight = (int)height.GetValue(profile);
}
cs



이때 PropertyInfo 클래스를 이용하는데요, PropertyInfo클래스는 SetValue() GetValue() 메소드를 가지고 있습니다.
SetValue() 메소드는 프로퍼티를 입력할 객체와 어떤 값을 넣어줄지를 인자로 받습니다. 뿐만 아니라 마지막 매개 변수는 인덱서의 인덱스를 위해
사용이 됩니다.
그리고 GetValue() 메소드를 통해 객체에서 찾고자하는 프로퍼티를 찾아 반환하게 되지요.


이번에는 리플렉션을 이용해 메소드를 호출해보겠습니다. 메소드의 정보를 담는 MethodInfo 클래스에는 Invoke()라는 메소드가 있습니다.
메소드를 이용하면 동적으로 메소드를 호출하는 것이 가능해집니다.

1
2
3
4
5
6
7
8
9
10
class Profile
{
    public string Name { get; set; }
    public int Height { get; set; }
    public void Print()
    {
        Console.WriteLine("{0}, {1}", Name, Height);
    }
}
cs

아까 예제로 보여드렸던 클래스에 Print()메소드를 추가했습니다. 이제 MethodInfo클래스를 통해 Print()함수를 호출해보겠습니다.

1
2
3
MethodInfo method = type.GetMethod("Print");
method.Invoke(profile, null);
cs

바로 위에서 GetValue(), SetValue() 사용했던 코드 다음에 추가될 부분만 적어보았습니다.
typeof 런타임중 동적으로 인스턴스를 생성하고, 만들어진 인스턴스의 프로퍼티에 값을 할당하며 마지막엔 Print() 메소드를 호출하게 됩니다.