当前位置:网站首页>In Net platform using reflectiondynamicobject to optimize reflection calling code

In Net platform using reflectiondynamicobject to optimize reflection calling code

2022-06-12 01:47:00 Soar, Yi

Encapsulation based principle ,API The designer of will put some members ( attribute 、 Field 、 Such method ) Hide to ensure robustness . But there are always situations where direct access to these private members is required .

To access private members of a type , In addition to changes API Design also uses reflection technology :

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);
}

This kind of writing is not elegant :

  1. The code is long , Trouble writing .
  2. The implementation is relatively simple , It's not very intuitive .

The author is based on “ Dynamic typing technology ” A relatively elegant scheme is explored to beautify the above code , And named it ReflectionDynamicObject :

void Main()
{
    var api = new MyApi();
    dynamic wrapper = ReflectionDynamicObject.Wrap(api);
    Console.WriteLine(wrapper._createdAt);
}

In addition to support getting values ,ReflectionDynamicObject Assignment is also supported :

void Main()
{
    var api = new MyApi();
    dynamic wrapper = ReflectionDynamicObject.Wrap(api);
    wrapper._createdAt = new DateTime(2022, 2, 2, 22, 22, 22);
    api.ShowCreateTime();
}

Except for the fields , Of course, operations on attributes are also supported :

void Main()
{
    var api = new MyApi();
    dynamic wrapper = ReflectionDynamicObject.Wrap(api);
    wrapper.ShowTimes = 100;
    Console.WriteLine(wraper.ShowTimes);
}

In support of attributes ,ReflectionDynamicObject Used “ A quick reflection ” technology , Delegate values and copy operations to optimize performance .

ReflectionDynamicObject Implementation principle of

ReflectionDynamicObject Derive from DynamicObject , All attributes and fields are obtained through reflection technology, and their getter and setter Method is stored and passed through TryGetMember and TrySetMember Method is called at run time .

ReflectionDynamicObject Source code

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 true;
        }
        return base.TrySetMember(binder, value);
    }

    #region  A quick reflection 
    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
    {
        private FieldInfo _field;
        public Setter(FieldInfo field)
        {
            _field = field ?? throw new ArgumentNullException(nameof(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));
        }
        public object Get(object instance)
        {
            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));
        }
        public void Set(object instance, object value)
        {
            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 The limitations of

Based on the consideration of complexity ,ReflectionDynamicObject Not added to “ Method ” Support for . This means that the call to the method is missing . Although dynamic behavior frees the program from its dependence on strings , But the implementation is right “ restructure ” Your support is still unfriendly .

Where to use ReflectionDynamicObject ?

Liquid Theme engine According to Liquid Language and Shopify Thematic mechanism and adopt Fluid A set of template engine implementation HTML Theme engine . The engine allows end users to freely modify their theme templates without affecting the host . The ultimate goal is to be multilingual 、 multiple-topic 、 High scalability and WYSIWYG .

Writing Liquid Theme engine when , The author needs to rewrite Fluid Template engine render The tag lets the child view from snippets Folder loading . When implementing this tag , Need to access TemplateContext Of LocalScope and RootScope Field , Unfortunately, the above fields are marked as internal , Cannot access in external assembly . So there was ReflectionDynamicObject , Help the author to complete LocalScope and RootScope The interview of .

Liquid Template language : https://www.coderbusy.com/liquid

Fluid template engine :https://github.com/sebastienros/fluid

Liquid Theme engine :https://gitee.com/zyingnet_kf/liquid-theme-engine

原网站

版权声明
本文为[Soar, Yi]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/03/202203011215123901.html