在 .NET 中存取私有/內部欄位與屬性,傳統反射雖通用但效能低落。NonPublicPropertyProxy 採用動態 IL 代理技術,效能較反射提升 3-4 個數量級,且支援實例/靜態欄位、實例屬性的讀寫,是高效能框架開發的利器。
DynamicMethodProxyFactory 僅支援方法呼叫,屬性雖可透過 get/set 方法存取,但欄位無對應方法,仍需反射| 功能 | 支援 | 備註 |
|---|---|---|
| 實例屬性讀寫 | ✅ | 透過 get/set 方法代理 |
| 實例欄位讀寫 | ✅ | 直接 IL 存取欄位位址 |
| 靜態欄位讀寫 | ✅ | 傳入 null 作為 instance |
| 靜態屬性讀寫 | ❌ | 需改用 DynamicMethodProxyFactory |
public class User
{
private string _name;
private int _id;
private UserStatus _status;
private List _children;
private static int _staticValue;
// 僅為測試用,實際專案中無公用 setter
public string Name => _name;
public int Id => _id;
}
var user = new User();
// 取得 Setter 代理(首次呼叫會產生 IL,後續複用極快)
var nameSetter = NonPublicPropertyProxyFactory.GetPropertySetter(typeof(User), "name");
var idSetter = NonPublicPropertyProxyFactory.GetPropertySetter(typeof(User), "id");
var statusSetter = NonPublicPropertyProxyFactory.GetPropertySetter(typeof(User), "status");
var childrenSetter = NonPublicPropertyProxyFactory.GetPropertySetter(typeof(User), "Children");
var staticSetter = NonPublicPropertyProxyFactory.GetPropertySetter(typeof(User), "staticValue");
// 呼叫代理委派
nameSetter(user, "Abu");
idSetter(user, 995006);
statusSetter(user, UserStatus.Admin);
childrenSetter(user, new List { "Child1", "Child2" });
staticSetter(null, 100); // 靜態欄位傳入 null
// 100 萬次迴圈測試 (Release, x64)
Reflection SetValue: ~1,200 ms
DynamicMethodProxy: ~0.3 ms (快 4000 倍)
NonPublicPropertyProxy: ~0.4 ms (快 3000 倍)
| 場景 | 建議 |
|---|---|
| 單次/低頻存取 | 直接用反射,避免 IL 產生一次性開銷 |
| ORM 實體映射 | ✅ 最佳選擇,啟動時快取代理,運行期極速 |
| 序列化/反序列化 | ✅ 大量物件圖處理效能顯著提升 |
| 單元測試私有狀態驗證 | ✅ 替代 PrivateObject,語法更簡潔 |
DynamicMethod 產生強型別存取委派ldfld/stfld 或屬性 call get/set 方法Action<TInstance, TValue> (Setter) / Func<TInstance, TValue> (Getter)Dictionary<Type, Dictionary<string, Delegate>> 快取,確保單一型別/成員僅編譯一次本元件萃取自 NBear.Mapping 專案的屬性存取邏輯,經重構獨立發布。完整原始碼可參考 GitHub 專案,包含單元測試與效能基準專案。
掌握動態 IL 代理技術,能在框架開發、高效能資料處理、測試自動化等領域脫離反射束縛,實現極致效能與優雅 API 的雙重保障。