C#反射优化

参考博文

用lambda表达式树替代反射

未参考博文

用Emit技术替代反射
C# 之 反射性能优化1 及后续文章

核心代码

    public class Property
    {

        private readonly PropertyGetter getter;
        private readonly PropertySetter setter;
        public string Name { get; private set; }
        public PropertyInfo Info { get; private set; }

        public Property(PropertyInfo propertyInfo)
        {
            if (propertyInfo == null) {
                throw new NullReferenceException("属性不能为空");
            }
            this.Name = propertyInfo.Name;
            this.Info = propertyInfo;
            this.getter = new PropertyGetter(propertyInfo);
            this.setter = new PropertySetter(propertyInfo);
        }

        public object GetValue(object instance)
        {
            if (getter != null)
            {
                return getter.Invoke(instance);
            }
            return null;
        }

        public void SetValue(object instance, object value)
        {
            if (setter != null) {
                setter.Invoke(instance, value);
            }
        }

        private class PropertyGetter
        {
            private readonly Func<object, object> funcGet;

            public PropertyGetter(PropertyInfo propertyInfo)
            {
                if (propertyInfo == null)
                {
                    throw new ArgumentNullException();
                }
                if (propertyInfo.DeclaringType == null) {
                    throw new ArgumentNullException();
                }
                if (propertyInfo.Name == null) {
                    throw new ArgumentNullException();
                }

                this.funcGet = CreateGetValueDelegate(propertyInfo.DeclaringType, propertyInfo.Name);
            }

            private static Func<object, object> CreateGetValueDelegate(Type declaType, String propertyName)
            {
                var param_instance = Expression.Parameter(typeof(object), "instance");
                var body_objToType = Expression.Convert(param_instance, declaType);
                var body_getTypeProperty = Expression.Property(body_objToType, propertyName);
                var body_return = Expression.Convert(body_getTypeProperty, typeof(object));
                return Expression.Lambda<Func<object, object>>(body_return, param_instance).Compile();
            }

            public object Invoke(object instance)
            {
                if (funcGet != null) {
                    return funcGet(instance);
                }
                return null;
            }
        }

        private class PropertySetter
        {
            private readonly Action<object, object> setFunc;

            public PropertySetter(PropertyInfo propertyInfo)
            {
                if (propertyInfo == null) {
                    throw new ArgumentNullException();
                }
                this.setFunc = CreateSetValueDelegate(propertyInfo);
            }

            private static Action<object, object> CreateSetValueDelegate(PropertyInfo propertyInfo)
            {
                var param_instance = Expression.Parameter(typeof(object), "instance");
                var param_value = Expression.Parameter(typeof(object), "value");
                var body_instance = Expression.Convert(param_instance, propertyInfo.DeclaringType);
                var body_value = Expression.Convert(param_value, propertyInfo.PropertyType);
                var body_call = Expression.Call(body_instance, propertyInfo.GetSetMethod(), body_value);
                return Expression.Lambda<Action<object, object>>(body_call, param_instance, param_value).Compile();
            }

            public void Invoke(object instance, object value)
            {
                if (setFunc != null)
                {
                    setFunc(instance, value);
                }
            }
        }
    }

性能比较

C#反射优化

  • 上图假设为读取100行100列的配置
    上方为纯赋值1W次,耗时112.38ms,
    下方为表达树,需要创建100个(与列数相同)Property类,执行1W次赋值,主要消耗是Property的创建,更具体是PropertySetter和PropertyGetter的创建
  • 行数越多,表达树性能越好,上例中,行数为87时,两者性能相当,行数少纯赋值好,行数多表达树好
  • 读取配置时,只需要属性赋值,可以不初始化PropertyGetter,表达树性能更好,行数超过50便好于纯赋值

其他

  • 感觉涉及到动态代码生成,未测试ios是否支持
  • 只支持属性Property,不支持字段FieldInfo
  • Invoke(object instance, object value)的instance是引用类型,应该没有装箱拆箱
    但value一般是值类型,会有装箱拆箱操作,不过也还好吧