在?.NET?平臺(tái)使用?ReflectionDynamicObject?優(yōu)化反射調(diào)用的代碼詳解
基于封裝的原則,API 的設(shè)計(jì)者會(huì)將部分成員(屬性、字段、方法等)隱藏以保證健壯性。但總有需要直接訪問(wèn)這些私有成員的情況。
為了訪問(wèn)一個(gè)類型的私有成員,除了更改 API 設(shè)計(jì)還有就是使用反射技術(shù):
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); }
這種寫法并不優(yōu)雅:
代碼冗長(zhǎng),編寫麻煩。實(shí)現(xiàn)比較繞,不太直觀。
筆者基于“動(dòng)態(tài)類型技術(shù)”探索出了一種相對(duì)來(lái)說(shuō)比較優(yōu)雅的方案用于美化上述代碼,并為其命名為 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(); }
除了字段,當(dāng)然也支持對(duì)屬性的操作:
void Main() { var api = new MyApi(); dynamic wrapper = ReflectionDynamicObject.Wrap(api); wrapper.ShowTimes = 100; Console.WriteLine(wraper.ShowTimes); }
在對(duì)屬性的支持上,ReflectionDynamicObject 使用了“快速反射”技術(shù),將取值和復(fù)制操作生成了委托以優(yōu)化性能。
ReflectionDynamicObject 的實(shí)現(xiàn)原理
ReflectionDynamicObject 派生自 DynamicObject ,其內(nèi)部通過(guò)反射技術(shù)獲取到所有的屬性和字段并對(duì)其 getter 和 setter 方法進(jìn)行存儲(chǔ)并通過(guò) TryGetMember 和 TrySetMember 方法經(jīng)運(yùn)行時(shí)調(diào)用。
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<T1, T2> : IGetter private readonly Func<T1, T2> _getter; public Getter(Func<T1, T2> getter) _getter = getter ?? throw new ArgumentNullException(nameof(getter)); return _getter((T1)instance); private class Setter<T1, T2> : ISetter private readonly Action<T1, T2> _setter; public Setter(Action<T1, T2> 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<string, IGetter>(); var setter = new SortedDictionary<string, ISetter>(); 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<string, IGetter> _getters; private readonly IReadOnlyDictionary<string, ISetter> _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<Type, Accessor> _accessors = new Dictionary<Type, Accessor>(); 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<Type, Accessor>(_accessors); temp[type] = new Accessor(type); _accessors = temp; return accessor; #endregion }
ReflectionDynamicObject 的局限性
基于復(fù)雜度的考慮,ReflectionDynamicObject 并未添加對(duì)“方法”的支持。這也就意味著對(duì)方法的調(diào)用是缺失的。雖然動(dòng)態(tài)行為讓程序擺脫了對(duì)字符串的依賴,但是該實(shí)現(xiàn)對(duì)“重構(gòu)”的支持仍然不友好。
哪里用到了 ReflectionDynamicObject ?
Liquid 主題引擎 是筆者根據(jù) Liquid 語(yǔ)言和 Shopify 主題機(jī)制并采用 Fluid 模板引擎實(shí)現(xiàn)的一套 HTML 主題引擎。該引擎允許最終用戶自由的修改自己的主題模板而不會(huì)對(duì)宿主造成影響。最終目標(biāo)是做到多語(yǔ)言、多主題、高擴(kuò)展性以及所見(jiàn)即所得。
在編寫 Liquid 主題引擎 時(shí),筆者需要重寫 Fluid 模板引擎的 render 標(biāo)簽讓子視圖從 snippets 文件夾加載。在實(shí)現(xiàn)該標(biāo)簽時(shí),需要訪問(wèn) TemplateContext 的 LocalScope 和 RootScope 字段,不幸的是上述字段被標(biāo)記為了 internal ,無(wú)法在外部程序集中訪問(wèn)到。于是便有了 ReflectionDynamicObject ,幫助筆者完成對(duì) LocalScope 和 RootScope 的訪問(wèn)。
參考鏈接
Liquid 模板語(yǔ)言: https://www.coderbusy.com/liquid
Fluid 模板引擎:https://github.com/sebastienros/fluid
Liquid 主題引擎:https://gitee.com/zyingnet_kf/liquid-theme-engine
到此這篇關(guān)于在 .NET 平臺(tái)使用 ReflectionDynamicObject 優(yōu)化反射調(diào)用代碼的文章就介紹到這了,更多相關(guān).NET ReflectionDynamicObject 優(yōu)化反射調(diào)用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net+js實(shí)時(shí)奧運(yùn)金牌榜代碼
運(yùn)期間,公司交給我一個(gè)任務(wù),在公司主頁(yè)上放上奧運(yùn)金牌榜的排名,之前的實(shí)現(xiàn)方式是采用ajax2008-09-09ASP.NET?MVC項(xiàng)目實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)無(wú)刷新
這篇文章介紹了ASP.NET?MVC項(xiàng)目實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)無(wú)刷新的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07充分利用ASP.NET的三種緩存提高站點(diǎn)性能的注意方法
充分利用ASP.NET的三種緩存提高站點(diǎn)性能的注意方法...2007-09-09IIS 瀏覽aspx頁(yè)面出現(xiàn)無(wú)法顯示XML頁(yè)的解決方法分享
這篇文章介紹了IIS 瀏覽aspx頁(yè)面出現(xiàn)無(wú)法顯示XML頁(yè)的解決方法,有需要的朋友可以參考一下2013-11-11asp.net不同頁(yè)面間數(shù)據(jù)傳遞的多種方法
這篇文章主要介紹了asp.net不同頁(yè)面間數(shù)據(jù)傳遞的多種方法,包括使用QueryString顯式傳遞、頁(yè)面對(duì)象的屬性、cookie、Cache等9種方法2014-01-01ASP.NET Core擴(kuò)展庫(kù)之日志功能的使用詳解
這篇文章主要介紹了ASP.NET Core擴(kuò)展庫(kù)之日志功能的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用.NET技術(shù),感興趣的朋友可以了解下2021-03-03ASP.NET:設(shè)置頁(yè)面buffer引出來(lái)的問(wèn)題
ASP.NET:設(shè)置頁(yè)面buffer引出來(lái)的問(wèn)題...2006-09-09未能加載文件或程序集“AspNetPager”或它的某一個(gè)依賴項(xiàng)。拒絕訪問(wèn)
突然間,訪問(wèn)站點(diǎn)所有頁(yè)面都出錯(cuò),全提示:未能加載文件或程序集“AspNetPager”或它的某一個(gè)依賴項(xiàng)。拒絕訪問(wèn)2012-06-06asp.net省市三級(jí)聯(lián)動(dòng)的DropDownList+Ajax的三種框架(aspnet/Jquery/ExtJs)示例
前段時(shí)間需要作一個(gè)的Web前端應(yīng)用,需要用多個(gè)框架,一個(gè)典型的應(yīng)用場(chǎng)景是省市三級(jí)聯(lián)動(dòng),基于此應(yīng)用,特將三種主要的ajax框架略作整理,方便有需要的朋友查閱。2010-06-06asp.net access web.config denied
如果出現(xiàn)這個(gè)問(wèn)題,最好首先檢查一下ASPNET的帳號(hào),是否有訪問(wèn)權(quán)限。2009-04-04