在?.NET?平臺使用?ReflectionDynamicObject?優(yōu)化反射調用的代碼詳解
基于封裝的原則,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);
}
這種寫法并不優(yōu)雅:
代碼冗長,編寫麻煩。實現(xiàn)比較繞,不太直觀。
筆者基于“動態(tài)類型技術”探索出了一種相對來說比較優(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();
}除了字段,當然也支持對屬性的操作:
void Main()
{
var api = new MyApi();
dynamic wrapper = ReflectionDynamicObject.Wrap(api);
wrapper.ShowTimes = 100;
Console.WriteLine(wraper.ShowTimes);
}在對屬性的支持上,ReflectionDynamicObject 使用了“快速反射”技術,將取值和復制操作生成了委托以優(yōu)化性能。
ReflectionDynamicObject 的實現(xiàn)原理
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<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 的局限性
基于復雜度的考慮,ReflectionDynamicObject 并未添加對“方法”的支持。這也就意味著對方法的調用是缺失的。雖然動態(tài)行為讓程序擺脫了對字符串的依賴,但是該實現(xiàn)對“重構”的支持仍然不友好。
哪里用到了 ReflectionDynamicObject ?
Liquid 主題引擎 是筆者根據 Liquid 語言和 Shopify 主題機制并采用 Fluid 模板引擎實現(xiàn)的一套 HTML 主題引擎。該引擎允許最終用戶自由的修改自己的主題模板而不會對宿主造成影響。最終目標是做到多語言、多主題、高擴展性以及所見即所得。
在編寫 Liquid 主題引擎 時,筆者需要重寫 Fluid 模板引擎的 render 標簽讓子視圖從 snippets 文件夾加載。在實現(xiàn)該標簽時,需要訪問 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
到此這篇關于在 .NET 平臺使用 ReflectionDynamicObject 優(yōu)化反射調用代碼的文章就介紹到這了,更多相關.NET ReflectionDynamicObject 優(yōu)化反射調用內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
ASP.NET MVC實現(xiàn)圖片上傳、圖片預覽顯示
這篇文章主要為大家詳細介紹了ASP.NET MVC實現(xiàn)圖片上傳、圖片預覽顯示的相關資料,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-05-05
ASP.NET?Core使用EF創(chuàng)建關系模型
這篇文章介紹了ASP.NET?Core使用EF創(chuàng)建關系模型的方法,文中通過示例代碼介紹的非常詳細。對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-04-04
Remoting和Webservice的詳細介紹及區(qū)別
這篇文章主要介紹了Remoting和Webservice的詳細介紹及區(qū)別的相關資料,需要的朋友可以參考下2016-11-11
ASP.NET MVC5網站開發(fā)之實現(xiàn)數(shù)據存儲層功能(三)
這篇文章主要為大家詳細介紹了ASP.NET MVC5網站開發(fā)之實現(xiàn)數(shù)據存儲層功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-08-08

