프로그램 실행 중에 타입 정보를 확인하고, 동적으로 조작할 수 있는 기능.
런타임에 클래스, 메서드, 필드 등의 정보를 가져오고 호출할 수 있다.
using System.Reflection; // namespace 필수
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Reflection");
string str = "Test_Method";
/* Reflection 활용하여 str 을 통해 Test_Method 를 호출할 수 있다. */
}
static void Test_Method()
{
...
}
}
Reflection 의 typeof() 와 GetType() 을 사용해 타입 정보를 확인할 수 있다.
컴파일 타임에 특정 타입의 System.Type 객체를 가져온다.
Type t = typeof(string);
Console.WriteLine(t.FullName); // System.String
런타임에 실제 객체의 타입 정보를 가져온다.
-반드시 인스턴스가 있어야 호출 가능하다.
string s = "Hi";
Type t = s.GetType();
Console.WriteLine(t.FullName); // System.String
using System.Reflection;
namespace ConsoleApp1
{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Example 1 : Reflection - Type");
Console.WriteLine();
// Class 자체의 타입 정보를 가져오기
Type t = typeof(Program);
Console.WriteLine($"Type Name : {t.Name}");
Console.WriteLine();
Console.WriteLine($"Type FullName : {t.FullName}");
Console.WriteLine();
Console.WriteLine($"Type Namespace: {t.Namespace}");
Console.WriteLine();
Console.WriteLine();
// Array 의 실제 인스턴스인 arr 의 런타임 타입을 가져오기
int[] arr = { 1, 2 };
Type arr_t = arr.GetType();
Console.WriteLine($"Type Name : {arr_t.Name}");
Console.WriteLine();
Console.WriteLine($"Type FullName : {arr_t.FullName}");
Console.WriteLine();
Console.WriteLine($"Type Namespace: {arr_t.Namespace}");
}
}
}
실행 결과 : Reflection 에서 Type 객체를 얻고,
타입 객체의 Name, FullName, Namespace 를 출력한다.
Type 객체를 얻은 뒤, 해당 타입이 가진 메서드, 프로퍼티, 필드, 생성자 등을 확인할 수 있다.
클래스에 정의된 메서드들의 정보를 가져온다.
클래스에 정의된 프로퍼티 정보를 가져온다.
using System.Reflection;
namespace ConsoleApp1
{
internal class Program
{
class Enemy
{
public string Name { get; set; }
public int Level { get; set; }
}
static void Main(string[] args)
{
Console.WriteLine("Example 2 : Reflection - Members");
Console.WriteLine();
Type t = typeof(Program);
Console.WriteLine("\n=== Program's Methods ===\n");
MethodInfo[] methods = t.GetMethods();
foreach (var method in methods)
{
Console.WriteLine($"{method.Name} (ReturnType: {method.ReturnType})");
}
Type e = typeof(Enemy);
Console.WriteLine("\n=== Enemy's Properties ===\n");
PropertyInfo[] properties= e.GetProperties();
foreach (var property in properties)
{
Console.WriteLine($"{property.Name} ({property.PropertyType})");
}
}
public void Test_Method()
{
Console.WriteLine("Test Method");
}
}
}
실행 결과 : Program 클래스에 정의된 public 메서드인 Test_Method 뿐 아니라,
ToString, Equals, GetHashCode, GetType 같은 .NET 기본 메서드들도 함께 출력한다.
Enemy 클래스의 Property 인 Name 과 Level 의 이름과 타입을 출력한다.
클래스에 정의된 필드 정보를 가져온다.
클래스의 생성자 정보를 가져온다.
using System.Reflection;
namespace ConsoleApp1
{
internal class Program
{
class Enemy
{
public string name;
private int level;
private float health;
public Enemy() { }
public Enemy(string name, int level, float health)
{
this.name = name;
this.level = level;
this.health = health;
}
}
static void Main(string[] args)
{
Console.WriteLine("Example 3 : Reflection - Members");
Console.WriteLine();
Enemy e = new Enemy();
Type t = e.GetType();
Console.WriteLine("\n=== Fields ===\n");
FieldInfo[] fields = t.GetFields(BindingFlags.Public
| BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in fields)
{
Console.WriteLine($"{field.Name} ({field.FieldType}) - {field.Attributes}");
}
Console.WriteLine("\n=== Constructors ===\n");
ConstructorInfo[] constructors = t.GetConstructors();
foreach (var constructor in constructors)
{
Console.WriteLine(constructor.ToString());
}
}
}
}
실행 결과 : 필드의 이름과 타입, 접근 지정자를 반환한다.
매개변수 없는 기본 생성자와 (string name, int level, float health) 를 받는 생성자 모두 반환된다.
C# 에서 생성자 정보를 Reflection 으로 확인했을 때 float 타입이 Single 로 출력되는 이유는
CLR(Common Language Runtime) 의 내부 타입 이름 때문이라고 한다.
(float → System.Single / bool → System.Boolean / long → System.Int64)
멤버를 검색할 때 범위와 조건을 지정하는 열거형
GetField(), GetMethods(), GetProperties() 은 기본적으로 public 인스턴스 멤버만 반환한다.
원하는 멤버만 가져오기 위해서 BindingFlags 를 통해 범위와 조건을 지정해주면 된다.
| 플래그 | 설명 |
|---|---|
| BindingFlags.Public | public 멤버만 검색 |
| BindingFlags.NonPublic | private, protected, internal 멤버 검색 |
| BindingFlags.Instance | 인스턴스 멤버 검색 |
| BindingFlags.Static | 정적 멤버 검색 |
| BindingFlags.DeclaredOnly | 상속받은 멤버 제외, 현재 타입에 선언된 것만 검색 |
FieldInfo[] fields = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
키-값 쌍으로 이루어진 csv 파일이나 텍스트 파일을 읽어와
해당 타입에 맞게 자동 파싱을 해서 넣을 수 있는 기능을 만들어 볼 수 있다.
using System.Reflection;
namespace ConsoleApp1
{
internal class Program
{
class ItemTable
{
public int ItemId { get; set; }
public string Name { get; set; }
public string Desc { get; set; }
}
static void Main(string[] args)
{
Console.WriteLine("Example 4 : Reflection");
Console.WriteLine();
string[] keys = { "ItemId", "Name", "Desc" };
string[] values = { "1", "검", "초보자용 기본 검" };
ItemTable itemTable = new ItemTable();
PropertyInfo[] propertyInfos = itemTable.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in propertyInfos)
{
Console.WriteLine($"{propertyInfo.PropertyType}, {propertyInfo.Name}");
for (int i = 0; i < keys.Length; i++)
{
if (keys[i].ToLower().Equals(propertyInfo.Name.ToLower()))
{
if (propertyInfo.PropertyType == typeof(int))
{
propertyInfo.SetValue(itemTable, int.Parse(values[i]));
}
else
{
propertyInfo.SetValue(itemTable, values[i]);
}
}
}
}
Console.WriteLine($"\nItemId : {itemTable.ItemId} , ItemName : {itemTable.Name} ,
Desc : {itemTable.Desc}");
}
}
}
실행 결과 : int 형인 ItemId 는 Parse 를 통해 string 을 int 로 바꿔서 넣어주고,
Name 과 Desc 는 string 값으로 입력되었다.
