Reflection là khi managed code có thể đọc metadata của chính nó để tìm assemblies. Về cơ bản, nó cho phép code inspect code khác trong cùng hệ thống. Với reflection trong C#, chúng ta có thể động tạo instance của một type và bind type đó vào object hiện có. Hơn nữa, chúng ta có thể lấy type từ object hiện có và truy cập properties của nó. Khi chúng ta dùng attributes trong code, reflection cho chúng ta quyền truy cập vì nó cung cấp object Type mô tả modules, assemblies, và types.
Giả sử chúng ta có property kiểu double và ở runtime chúng ta thực sự có giá trị là string và gán nó cho property sau khi đổi type. Chúng ta có thể dùng Convert.ChangeType() - Nó cho phép dùng thông tin runtime trên bất kỳ type IConvertible nào để thay đổi representation format.
Ví dụ
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; }
}
}
Output
Radius: 6.5
Trong ví dụ trên, ta thấy giá trị string "6.5" được convert sang kiểu double thực tế dùng Convert.ChangeType và gán cho property Radius dùng reflection tại runtime.
Các trường hợp sử dụng phổ biến
1. Convert từ string sang các kiểu primitive
public static void SetPropertyFromString(object obj, string propertyName, string value) {
var prop = obj.GetType().GetProperty(propertyName);
if (prop != null && prop.CanWrite) {
var convertedValue = Convert.ChangeType(value, prop.PropertyType);
prop.SetValue(obj, convertedValue);
}
}
// Sử dụng
var person = new Person();
SetPropertyFromString(person, "Age", "25"); // int
SetPropertyFromString(person, "Height", "5.9"); // double
SetPropertyFromString(person, "IsActive", "true"); // bool
SetPropertyFromString(person, "BirthDate", "1990-01-15"); // DateTime
2. Xử lý nullable types
public static void SetPropertySafe(object obj, string propertyName, string value) {
var prop = obj.GetType().GetProperty(propertyName);
if (prop == null || !prop.CanWrite) return;
Type targetType = prop.PropertyType;
// Xử lý nullable type
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Nullable<>)) {
targetType = Nullable.GetUnderlyingType(targetType);
}
if (string.IsNullOrEmpty(value)) {
prop.SetValue(obj, null);
return;
}
var convertedValue = Convert.ChangeType(value, targetType);
prop.SetValue(obj, convertedValue);
}
3. Sử dụng TypeConverter cho custom type
public static void SetPropertyWithTypeConverter(object obj, string propertyName, string value) {
var prop = obj.GetType().GetProperty(propertyName);
if (prop == null || !prop.CanWrite) return;
var converter = TypeDescriptor.GetConverter(prop.PropertyType);
if (converter.CanConvertFrom(typeof(string))) {
var convertedValue = converter.ConvertFromInvariantString(value);
prop.SetValue(obj, convertedValue);
} else {
// Fallback to Convert.ChangeType
var convertedValue = Convert.ChangeType(value, prop.PropertyType);
prop.SetValue(obj, convertedValue);
}
}
Lưu ý quan trọng
- Convert.ChangeType chỉ hoạt động với các type implement
IConvertible(primitive types, DateTime, Enum, Guid, v.v.) - Với custom type, cần dùng
TypeConverterhoặc parse thủ công - Luôn kiểm tra
prop.CanWritetrước khi SetValue - Xử lý exception (FormatException, OverflowException, ArgumentNullException)
- Performance: Reflection chậm hơn direct access, cân nhắc cache PropertyInfo