Reflection cho phép kiểm tra và thao tác metadata/type tại runtime. Namespace System.Reflection.
Các bước thiết lập giá trị property
- Lấy
Typecủa object (obj.GetType()hoặctypeof(T)). - Lấy
PropertyInfoquatype.GetProperty("PropertyName"). - Gọi
propertyInfo.SetValue(obj, value, index).
Ví dụ cơ bản
using System;
using System.Reflection;
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
class Program
{
static void Main()
{
User user = new User { Id = 1, Name = "Old", Email = "old@test.com" };
// Lấy PropertyInfo
Type type = user.GetType();
PropertyInfo nameProp = type.GetProperty("Name");
PropertyInfo emailProp = type.GetProperty("Email");
// Set value
nameProp.SetValue(user, "New Name");
emailProp.SetValue(user, "new@test.com");
Console.WriteLine($"{user.Id}: {user.Name} - {user.Email}");
// Output: 1: New Name - new@test.com
Console.ReadLine();
}
}
Các overload của SetValue
// 1. Basic
propertyInfo.SetValue(obj, value);
// 2. Với index (cho indexed property / indexer)
propertyInfo.SetValue(obj, value, new object[] { index });
// 3. Với BindingFlags (private, non-public)
PropertyInfo privateProp = type.GetProperty("InternalId", BindingFlags.NonPublic | BindingFlags.Instance);
privateProp.SetValue(obj, 999);
Generic Helper Method
public static class ReflectionHelper
{
public static void SetPropertyValue<T>(T obj, string propertyName, object value)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName));
PropertyInfo prop = typeof(T).GetProperty(propertyName,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (prop == null)
throw new ArgumentException($"Property '{propertyName}' not found on {typeof(T).Name}");
if (!prop.CanWrite)
throw new InvalidOperationException($"Property '{propertyName}' is read-only");
// Convert type nếu cần (string -> int, etc.)
object convertedValue = Convert.ChangeType(value, prop.PropertyType);
prop.SetValue(obj, convertedValue);
}
}
// Usage
var user = new User();
ReflectionHelper.SetPropertyValue(user, "Name", "John");
ReflectionHelper.SetPropertyValue(user, "Age", "25"); // Auto convert string->int
Performance: Compile Delegate (Fast)
// Cache delegate cho performance cao (ví dụ: mapping hàng loạt)
public static class FastPropertySetter
{
private static readonly Dictionary> _setters = new();
public static void SetValue(object obj, string propertyName, object value)
{
string key = $"{obj.GetType().FullName}.{propertyName}";
if (!_setters.TryGetValue(key, out var setter))
{
var prop = obj.GetType().GetProperty(propertyName);
var instance = Expression.Parameter(typeof(object), "instance");
var val = Expression.Parameter(typeof(object), "value");
var castInstance = Expression.Convert(instance, obj.GetType());
var castValue = Expression.Convert(val, prop.PropertyType);
var call = Expression.Call(castInstance, prop.GetSetMethod(), castValue);
setter = Expression.Lambda>(call, instance, val).Compile();
_setters[key] = setter;
}
setter(obj, value);
}
}
Lưu ý
- Reflection chậm hơn direct call ~100-1000x. Cache delegate nếu gọi nhiều.
BindingFlags.NonPublic | BindingFlags.Instanceđể set private/internal property.- Property phải có
setaccessor (CanWrite == true). Convert.ChangeTypekhông hoạt động với nullable type (int?) -> cần handle riêng.