Reflection ใน .NET ใช้ Namespace System.Reflection เพื่อตรวจสอบและจัดการ Metadata ของ Types, Properties, Methods, Fields ใน Runtime
ขั้นตอนการ Set Property ผ่าน Reflection
- ดึง
Typeของ Object (obj.GetType()หรือtypeof(T)) - หา
PropertyInfoด้วยGetProperty(name) - เรียก
SetValue(obj, value, index)
ตัวอย่างพื้นฐาน
using System;
using System.Reflection;
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
static void Main()
{
User user = new User { Id = 1, Name = "John", Email = "john@example.com" };
// 1. Get Type
Type type = user.GetType();
// 2. Get PropertyInfo (Public Instance Properties)
PropertyInfo nameProp = type.GetProperty("Name");
PropertyInfo emailProp = type.GetProperty("Email");
if (nameProp != null && nameProp.CanWrite)
{
// 3. Set Value
nameProp.SetValue(user, "Jane");
Console.WriteLine($"Name changed to: {user.Name}"); // Jane
}
if (emailProp != null && emailProp.CanWrite)
{
emailProp.SetValue(user, "jane@example.com");
Console.WriteLine($"Email changed to: {user.Email}");
}
// Set โดยใช้ Object (Boxing สำหรับ Value Types)
PropertyInfo idProp = type.GetProperty("Id");
idProp?.SetValue(user, 999);
Console.WriteLine($"Id changed to: {user.Id}"); // 999
}
ตัวอย่างขั้นสูง: Dynamic Object Mapper
public static class ObjectMapper
{
public static void MapProperties(object source, object target, bool ignoreCase = true)
{
var sourceProps = source.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var targetProps = target.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
var comparison = ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
foreach (var srcProp in sourceProps)
{
if (!srcProp.CanRead) continue;
var tgtProp = Array.Find(targetProps, p =>
p.Name.Equals(srcProp.Name, comparison) && p.CanWrite &&
p.PropertyType.IsAssignableFrom(srcProp.PropertyType));
if (tgtProp != null)
{
object value = srcProp.GetValue(source);
tgtProp.SetValue(target, value);
}
}
}
}
// ใช้งาน: Copy Properties ระหว่าง Object แบบ Dynamic
var dto = new UserDto { Id = 1, Name = "John", Email = "john@test.com", Extra = "Ignore" };
var entity = new UserEntity();
ObjectMapper.MapProperties(dto, entity); // Copy Id, Name, Email
BindingFlags สำคัญ
| Flag | ความหมาย |
|---|---|
Public |
Public Members |
NonPublic |
Private/Protected/Internal Members |
Instance |
Instance Members (Non-static) |
Static |
Static Members |
DeclaredOnly |
เฉพาะที่ประกาศใน Type นี้ (ไม่รวม Base Class) |
// หา Private Property
PropertyInfo privateProp = type.GetProperty("Secret", BindingFlags.NonPublic | BindingFlags.Instance);
privateProp?.SetValue(obj, "New Secret", null); // index = null สำหรับ Non-indexed Property
Indexed Properties (Indexers)
public class Sample
{
public string this[int index] { get; set; }
}
var sample = new Sample();
PropertyInfo indexer = typeof(Sample).GetProperty("Item"); // Default Name = "Item"
indexer.SetValue(sample, "Value", new object[] { 0 }); // Index = 0
Console.WriteLine(sample[0]); // "Value"
Performance Considerations
- Reflection ช้ากว่า Direct Access 10-100x
- Cache
PropertyInfoไว้ใช้ซ้ำ (Static Dictionary<Type, PropertyInfo[]>) - ใช้
Delegate.CreateDelegateหรือExpression Treesสร้าง Fast Setter - Libraries: FastMember, Mapster, AutoMapper ใช้ Expression Trees เร็วกว่า
Fast Setter ด้วย Expression Tree (Cacheable)
public static class FastReflection
{
private static readonly ConcurrentDictionary<(Type, string), Action
ข้อควรระวัง
NullReferenceExceptionหาก Property ไม่มี (GetProperty คืน null)ArgumentExceptionหาก Type ไม่ตรง (Value Type ต้อง Boxing)TargetExceptionหาก Object เป็น Null หรือ Type ไม่ตรง- Security: Partial Trust อาจจำกัด Reflection