在.NET平台怎么使用ReflectionDynamicObject优化反射
本文小编为大家详细介绍“在.NET平台怎么使用ReflectionDynamicObject优化反射”,内容详细,步骤清晰,细节处理妥当,希望这篇“在.NET平台怎么使用ReflectionDynamicObject优化反射”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。
基于封装的原则,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<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 并未添加对“方法”的支持。这也就意味着对方法的调用是缺失的。虽然动态行为让程序摆脱了对字符串的依赖,但是该实现对“重构”的支持仍然不友好。
哪里用到了 ReflectionDynamicObject ?
Liquid 主题引擎 是笔者根据 Liquid 语言和 Shopify 主题机制并采用 Fluid 模板引擎实现的一套 HTML 主题引擎。该引擎允许最终用户自由的修改自己的主题模板而不会对宿主造成影响。最终目标是做到多语言、多主题、高扩展性以及所见即所得。
读到这里,这篇“在.NET平台怎么使用ReflectionDynamicObject优化反射”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341