리플렉션(Reflection)은 관리 코드(managed code)가 자신의 메타데이터를 읽어 어셈블리를 찾을 수 있도록 하는 기능입니다. 쉽게 말해, 동일한 시스템 안에서 코드가 다른 코드를 검사하고 조작할 수 있게 해주는 메커니즘입니다. C#에서 리플렉션을 활용하면 특정 타입의 인스턴스를 동적으로 생성하거나, 해당 타입을 기존 객체에 바인딩할 수 있습니다. 또한 이미 존재하는 객체로부터 타입 정보를 가져와 그 속성(property)에 접근하는 것도 가능합니다. 코드에서 애트리뷰트(attribute)를 사용할 때에도 리플렉션은 모듈, 어셈블리, 타입을 설명하는 Type 객체를 제공하기 때문에 이들에 대한 접근이 가능합니다.
그렇다면 실제 개발에서 자주 마주치는 상황을 생각해 보겠습니다. 속성의 타입은 double인데, 런타임에 전달되는 값은 문자열(string) 형태라면 어떻게 해야 할까요? 이 경우 값을 적절한 타입으로 변환한 뒤 속성에 할당해야 합니다. 이때 유용하게 사용할 수 있는 것이 바로 Convert.ChangeType() 메서드입니다. 이 메서드는 IConvertible 인터페이스를 구현하는 모든 타입에 대해 런타임 정보를 활용하여 표현 형식을 변경할 수 있도록 지원합니다.
예제 코드
using System;
using System.Reflection;
namespace DemoApplication{
class Program{
static void Main(){
Circle circle = new Circle();
string value = "6.5";
PropertyInfo propertyInfo = circle.GetType().GetProperty("Radius");
propertyInfo.SetValue(circle, Convert.ChangeType(value,
propertyInfo.PropertyType), null);
var radius = circle.GetType().GetProperty("Radius").GetValue(circle, null);
Console.WriteLine($"Radius: {radius}");
Console.ReadLine();
}
}
class Circle{
public double Radius { get; set; }
}
}
실행 결과
Radius: 6.5
위 예제의 실행 흐름을 살펴보겠습니다. 먼저 GetType().GetProperty("Radius")를 통해 Radius 속성의 PropertyInfo를 가져옵니다. 그다음 Convert.ChangeType()이 문자열 "6.5"를 속성의 실제 타입인 double로 변환하고, SetValue()를 통해 런타임에 해당 값을 Radius 속성에 동적으로 할당합니다. 마지막으로 GetValue()로 값을 다시 읽어 출력하면 정상적으로 6.5가 반환되는 것을 확인할 수 있습니다.
참고 사항
Convert.ChangeType()은 대상 타입이 IConvertible을 구현해야만 동작합니다. int, double, decimal, DateTime 등 기본 타입은 대부분 이 인터페이스를 구현하고 있으므로 문제없이 사용할 수 있습니다. 반면, 직접 정의한 클래스처럼 IConvertible을 구현하지 않는 타입에는 이 방법을 적용할 수 없으므로, 이 경우에는 타입별 컨버터를 별도로 구현하거나 TypeConverter를 활용하는 것이 좋습니다. 또한 변환에 실패할 경우 InvalidCastException이 발생할 수 있으므로, 외부 입력값을 처리할 때는 try-catch 블록이나 TryParse 계열 메서드와 함께 사용하는 것이 안전합니다.