網站首頁 編程語言 正文
基于封裝的原則,API 的設計者會將部分成員(屬性、字段、方法等)隱藏以保證健壯性。但總有需要直接訪問這些私有成員的情況。
為了訪問一個類型的私有成員,除了更改 API 設計還有就是使用反射技術:
public class MyApi { public MyApi() { _createdAt = DateTime.Now; } private DateTime _createdAt; public int ShowTimes { get; private set; } public void ShowCreateTime() { Console.WriteLine(_createdAt); ShowTimes++; } } void Main() { var api = new MyApi(); var field = api.GetType().GetField("_createdAt", BindingFlags.NonPublic | BindingFlags.Instance); var value = field.GetValue(api); Console.WriteLine(value); }
這種寫法并不優雅:
代碼冗長,編寫麻煩。實現比較繞,不太直觀。
筆者基于“動態類型技術”探索出了一種相對來說比較優雅的方案用于美化上述代碼,并為其命名為 ReflectionDynamicObject :
void Main() { var api = new MyApi(); dynamic wrapper = ReflectionDynamicObject.Wrap(api); Console.WriteLine(wrapper._createdAt); }
除了支持獲取值,ReflectionDynamicObject 還支持賦值:
void Main() { var api = new MyApi(); dynamic wrapper = ReflectionDynamicObject.Wrap(api); wrapper._createdAt = new DateTime(2022, 2, 2, 22, 22, 22); api.ShowCreateTime(); }
除了字段,當然也支持對屬性的操作:
void Main() { var api = new MyApi(); dynamic wrapper = ReflectionDynamicObject.Wrap(api); wrapper.ShowTimes = 100; Console.WriteLine(wraper.ShowTimes); }
在對屬性的支持上,ReflectionDynamicObject 使用了“快速反射”技術,將取值和復制操作生成了委托以優化性能。
ReflectionDynamicObject 的實現原理
ReflectionDynamicObject 派生自 DynamicObject ,其內部通過反射技術獲取到所有的屬性和字段并對其 getter 和 setter 方法進行存儲并通過 TryGetMember 和 TrySetMember 方法經運行時調用。
ReflectionDynamicObject 的源代碼
public sealed class ReflectionDynamicObject : DynamicObject { private readonly object _instance; private readonly Accessor _accessor; private ReflectionDynamicObject(object instance) { _instance = instance ?? throw new ArgumentNullException(nameof(instance)); _accessor = GetAccessor(instance.GetType()); } public static ReflectionDynamicObject Wrap(Object value) if (value == null) throw new ArgumentNullException(nameof(value)); return new ReflectionDynamicObject(value); public override bool TryGetMember(GetMemberBinder binder, out object result) if (_accessor.TryFindGetter(binder.Name, out var getter)) { result = getter.Get(_instance); return true; } return base.TryGetMember(binder, out result); public override bool TrySetMember(SetMemberBinder binder, object value) if (_accessor.TryFindSetter(binder.Name, out var setter)) setter.Set(_instance, value); return base.TrySetMember(binder, value); #region 快速反射 private interface IGetter object Get(object instance); private interface ISetter void Set(object instance, object value); private class Getter : IGetter private FieldInfo _field; public Getter(FieldInfo field) _field = field ?? throw new ArgumentNullException(nameof(field)); public object Get(object instance) return _field.GetValue(instance); private class Setter : ISetter public Setter(FieldInfo field) public void Set(object instance, object value) _field.SetValue(instance, value); private class Getter: IGetter private readonly Func _getter; public Getter(Func getter) _getter = getter ?? throw new ArgumentNullException(nameof(getter)); return _getter((T1)instance); private class Setter : ISetter private readonly Action _setter; public Setter(Action setter) this._setter = setter ?? throw new ArgumentNullException(nameof(setter)); this._setter.Invoke((T1)instance, (T2)value); private class Accessor public Accessor(Type type) this._type = type ?? throw new ArgumentNullException(nameof(_type)); var getter = new SortedDictionary (); var setter = new SortedDictionary (); var fields = _type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var field in fields) { getter[field.Name] = new Getter(field); setter[field.Name] = new Setter(field); } var props = _type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (var item in props) if (item.CanRead) { var method = item.GetMethod; var funcType = typeof(Func<,>).MakeGenericType(item.DeclaringType, item.PropertyType); var func = method.CreateDelegate(funcType); var getterType = typeof(Getter<,>).MakeGenericType(item.DeclaringType, item.PropertyType); var get = (IGetter)Activator.CreateInstance(getterType, func); getter[item.Name] = get; } if (item.CanWrite) var method = item.SetMethod; var actType = typeof(Action<,>).MakeGenericType(item.DeclaringType, item.PropertyType); var act = method.CreateDelegate(actType); var setterType = typeof(Setter<,>).MakeGenericType(item.DeclaringType, item.PropertyType); var set = (ISetter)Activator.CreateInstance(setterType, act); setter[item.Name] = set; _getters = getter; _setters = setter; private readonly Type _type; private readonly IReadOnlyDictionary _getters; private readonly IReadOnlyDictionary _setters; public bool TryFindGetter(string name, out IGetter getter) => _getters.TryGetValue(name, out getter); public bool TryFindSetter(string name, out ISetter setter) => _setters.TryGetValue(name, out setter); private static Dictionary _accessors = new Dictionary (); private static object _accessorsLock = new object(); private static Accessor GetAccessor(Type type) if (_accessors.TryGetValue(type, out var accessor)) return accessor; lock (_accessorsLock) if (_accessors.TryGetValue(type, out accessor)) return accessor; accessor = new Accessor(type); var temp = new Dictionary (_accessors); temp[type] = new Accessor(type); _accessors = temp; return accessor; #endregion }
ReflectionDynamicObject 的局限性
基于復雜度的考慮,ReflectionDynamicObject 并未添加對“方法”的支持。這也就意味著對方法的調用是缺失的。雖然動態行為讓程序擺脫了對字符串的依賴,但是該實現對“重構”的支持仍然不友好。
哪里用到了 ReflectionDynamicObject ?
Liquid 主題引擎 是筆者根據 Liquid 語言和 Shopify 主題機制并采用 Fluid 模板引擎實現的一套 HTML 主題引擎。該引擎允許最終用戶自由的修改自己的主題模板而不會對宿主造成影響。最終目標是做到多語言、多主題、高擴展性以及所見即所得。
在編寫 Liquid 主題引擎 時,筆者需要重寫 Fluid 模板引擎的 render 標簽讓子視圖從 snippets 文件夾加載。在實現該標簽時,需要訪問 TemplateContext 的 LocalScope 和 RootScope 字段,不幸的是上述字段被標記為了 internal ,無法在外部程序集中訪問到。于是便有了 ReflectionDynamicObject ,幫助筆者完成對 LocalScope 和 RootScope 的訪問。
參考鏈接
Liquid 模板語言: https://www.coderbusy.com/liquid
Fluid 模板引擎:https://github.com/sebastienros/fluid
Liquid 主題引擎:https://gitee.com/zyingnet_kf/liquid-theme-engine
原文鏈接:https://www.cnblogs.com/Soar1991/archive/2022/03/01/15949370.html
相關推薦
- 2022-04-10 Qt實現部件透明及陰影效果的示例代碼_C 語言
- 2022-07-09 Tensorflow中使用cpu和gpu有什么區別_python
- 2022-12-08 詳解C++引用變量時那些你不知道的東西_C 語言
- 2022-09-13 關于c語言中輸出字符指針的相關問題_C 語言
- 2022-04-23 配置基于域名訪問的網站以及指定用戶可訪問的網站
- 2022-07-06 C++實現中值濾波的示例代碼_C 語言
- 2023-05-10 Python中的SOLID原則實例詳解_python
- 2022-11-23 Qt采用線程以隊列方式實現下發數據_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支