[C#] 리플렉션 - Reflection
리플렉션은 어셈블리, 모듈 및 형식을 설명하는 개체(Type 형식)를 제공합니다. 리플렉션을 사용하면 동적으로 형식 인스턴스를 만들거나, 형식을 기존 개체에 바인딩하거나, 기존 개체에서 형식을 가져와 해당 메서드를 호출하거나, 필드 및 속성에 액세스할 수 있습니다. 코드에서 특성을 사용하는 경우 리플렉션은 특성에 대한 액세스를 제공합니다. [MSDN]
최근에 툴을 만들다가 꼭 필요한 기능에 대해서 떠올리다가 언제인가 오래전에 C# 책에서 봤던 Reflection 개념이 떠올라 사용하여 한번 작성해 보고 싶었습니다. 처음에 Reflection을 공부할때 이걸 그래서 어디다가 쓰지? 라는 생각을 많이 하였는데 막상 쓸때가 오기는 오더라구요.
사용법은 여러가지가 있는데 일단 개체가 제공하는 Type을 이용하여 사용하게 되고 이에따라 여러가지가 가능합니다.
우선 기본적인 사용법에 대해서 알아보겠습니다.
Type type = typeof(int);
기본적으로 위와 같이 Type이라고 하면 어떤 인스턴스의 정확한 런타임 형식을 나타냅니다.
(저기서 type은 보기만 해도 어떤 형식인지 알 수 있을 것만 같습니다만...)
인스턴스의 정확한 런타임 형식을 알면 바로 해당 형식은 사용하여 여러가지를 할 수 있는데 우선 메서드 리스트를 알 수 있습니다.
Type type = typeof(int);
var methodsList = type.GetMethods();
foreach(var list in methodsList)
{
Console.WriteLine(list);
}
//결과
Int32 CompareTo(System.Object)
Int32 CompareTo(Int32)
Boolean Equals(System.Object)
Boolean Equals(Int32)
Int32 GetHashCode()
System.String ToString()
System.String ToString(System.String)
System.String ToString(System.IFormatProvider)
System.String ToString(System.String, System.IFormatProvider)
Int32 Parse(System.String)
Int32 Parse(System.String, System.Globalization.NumberStyles)
Int32 Parse(System.String, System.IFormatProvider)
Int32 Parse(System.String, System.Globalization.NumberStyles, System.IFormatProvider)
Boolean TryParse(System.String, Int32 ByRef)
Boolean TryParse(System.String, System.Globalization.NumberStyles, System.IFormatProvider, Int32 ByRef)
System.TypeCode GetTypeCode()
System.Type GetType()
또한 알아낸 메서드를 직접 호출하는 것도 가능하죠.
string numbers = "12345";
object[] arg = { numbers, null };
var parse = typeof(int).GetMethod("TryParse", new[] { typeof(string), typeof(int).MakeByRefType() });
var result = parse.Invoke(null, arg);
Console.WriteLine(result);
//결과 : True
위와같이 int형식의 타입이 기본적으로 제공하는 "TryParse" 메서드를 가져와서 사용할 수 있게 됩니다.
string 형식의 Trim이나 또는 다른 어떠한 형식의 메서드가 있다면 사용할 수 있게 되지요.
하지만 여기까지는 그냥 int.TryParse를 쓰면 되는거 아닌가? 하는 생각이 들게 됩니다.
사실 저도 그랬으니까요..그렇다면 임의의 커스텀한 클래스를 사용해보면 어떨까요?
public static class Person
{
public static string name;
public static void Say()
{
Console.WriteLine("Hello Wolrd ");
}
}
위와 같은 클래스가 있다고 가정을 하고 그리고 아래와 같은 코드를 실행한다면 어떻게 될까요.
var t = typeof(Person);
var method1 = t.GetMethod("Say");
method1.Invoke(null, null);
//출력 결과 Hello Wolrd
신기하게도 함수만 가져와서 사용할 수 있게 됩니다.
그래도 여기까지와서도 생각해보면 static 인데 그냥 호출하면 되는거 아니야? 라는 생각이 다시 듭니다.
public class TestClass
{
public int number;
}
public class TestClass2
{
public int number;
}
자 다시 위와같은 클래스가 2개가 있습니다. 그럼 위에 2가지의 클래스중 아무거나 넣어도 인스턴스를 생성하고
필드를 가져와서 값을 채우고 그 리스트를 반환해줄 수 있다면?
void CreateActivatorInstance<T>(Type type, List<T> n )
{
FieldInfo[] dataFieldInfo;
dataFieldInfo = type.GetFields(BindingFlags.NonPublic
| BindingFlags.Instance
| BindingFlags.Public);
for ( int i = 0; i < 3; i++)
{
T data = (T)Activator.CreateInstance(typeof(T));
dataFieldInfo[0].SetValue(data, i);
n.Add(data);
}
}
List<TestClass> testList = new List<TestClass>();
List<TestClass2> testList2 = new List<TestClass2>();
CreateActivatorInstance(typeof(TestClass), testList);
CreateActivatorInstance(typeof(TestClass2), testList2);
foreach (var text in testList)
{
Console.WriteLine(text.number);
}
foreach (var text in testList2)
{
Console.WriteLine(text.number);
}
//결과
0
1
2
0
1
2
Reflection을 사용하면 인스턴스의 정확한 런타임 형식을 사용하여 클래스의 변수 목록, 함수 호출, 인스턴스 생성 등의 다양한
프로그래밍이 가능해집니다. 이런 기능들을 활용한다면 더 풍부하고 다양한 기능을 활용할 수 있을 것 같습니다.
[참고] https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/concepts/reflection
'프로그래밍 > C#' 카테고리의 다른 글
[C#] event 키워드 (0) | 2016.12.23 |
---|---|
[C#] Serialization - 객체 직렬화 (0) | 2016.10.29 |
[C#] yield 키워드 (0) | 2016.10.29 |
[C#] where & Generics (형식 제약 조건) (0) | 2016.10.29 |
[C#] 무명 메서드(Anonymous Methods) (0) | 2016.09.29 |