通过表达式树实现,根据名称获取或设置对象属性值(性能相较于反射而言要快1~2倍)

一、介绍

  • 在日常的开发过程中会出现,需要根据名称来获取某个未知对象的属性,常用的方式是使用反射来完成此类效果,但是反射在性能方面要比较差,所以这个组件是使用反射+表达式树来实现的获取、设置属性值,在性能上要比纯反射速度要快1~2倍
  • 内部实现原理:
    1.根据传入类型反射获取是否有此属性类型相等或支持隐式转换或者里式转换
    2.根据反射的属性信息生成表达式树,编译成函数存入字典
    3.下次再获取就直接在字典中获取之前编译好的函数进行执行,这样就可以获得几近于原生代码的速度来获取或设置属性值

二、测试

1.测试代码

class Program
{
    static void Main(string[] args)
    {
        object o = new MyClass
        {
            Id = 113231,
            My = new MyClass
            {
                Id = 123
            }
        };
        Console.WriteLine("---------获取属性值---------");
        long l = ObjectUtil<long>.GetPropertyValue(o, "Id");
        decimal d = ObjectUtil<decimal>.GetPropertyValue(o, "Id"); //获取支持隐式转换
        BaseMyClass mc = ObjectUtil<BaseMyClass>.GetPropertyValue(o, "My");
        IMyClass imc = ObjectUtil<IMyClass>.GetPropertyValue(o, "My"); //获取支持里式转换
        Console.WriteLine(l);
        Console.WriteLine(d);
        Console.WriteLine(mc);
        Console.WriteLine(imc);
        Console.WriteLine("---------设置属性值---------");
        ObjectUtil<long>.SetPropertyValue(o, "Id", 999L);
        ObjectUtil<int>.SetPropertyValue(o, "Id", 888); //设置支持隐式转换
        ObjectUtil<MyClass>.SetPropertyValue(o, "My", new MyClass
        {
            Id = 777
        }); //设置支持里式转换
        Console.WriteLine("设置完成后o的值:{0}", o);
        Console.WriteLine("---------读取属性值【性能测试100万次】---------");
        for (int x = 0; x < 4; x++)
        {
            var sw = Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                ObjectUtil<long>.GetPropertyValue(o, "Id");
            }

            sw.Stop();
            Console.WriteLine("表达式树耗时:{0}ms", sw.Elapsed.TotalMilliseconds);
            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                GetPropertyValue<long>(o, "Id");
            }

            sw.Stop();
            Console.WriteLine("反射耗时:{0}ms", sw.Elapsed.TotalMilliseconds);
        }

        Console.WriteLine("---------设置属性值【性能测试100万次】---------");

        for (int x = 0; x < 4; x++)
        {
            var sw = Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                ObjectUtil<long>.SetPropertyValue(o, "Id", i);
            }

            sw.Stop();
            Console.WriteLine("表达式树耗时:{0}ms", sw.Elapsed.TotalMilliseconds);
            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            {
                SetPropertyValue<long>(o, "Id", i);
            }

            sw.Stop();
            Console.WriteLine("反射耗时:{0}ms", sw.Elapsed.TotalMilliseconds);
        }
    }

    /// <summary>
    /// 反射设置属性值
    /// </summary>
    private static void SetPropertyValue<T>(object o, string name, T value)
    {
        var type = o.GetType();
        var info = type.GetProperty(name);
        if (info == null)
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”的公共属性!", type, name));
        if (info.PropertyType != typeof(T))
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”的公共属性!", type, name, typeof(T)));
        if (!info.CanWrite)
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”且可写的公共属性!", type, name, typeof(T)));
        info.SetValue(o, value);
    }

    /// <summary>
    /// 反射获取属性值
    /// </summary>
    private static T GetPropertyValue<T>(object o, string name)
    {
        var type = o.GetType();
        var info = type.GetProperty(name);
        if (info == null)
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”的公共属性!", type, name));
        if (info.PropertyType != typeof(T))
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”的公共属性!", type, name, typeof(T)));
        if (!info.CanRead)
            throw new Exception(string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”且可读的公共属性!", type, name, typeof(T)));
        return (T) info.GetValue(o);
    }

    public interface IMyClass
    {
    }

    public abstract class MyClassAbstract
    {
    }

    public class BaseMyClass : MyClassAbstract, IMyClass
    {
    }

    public class MyClass : BaseMyClass
    {
        public long Id { get; set; }

        public BaseMyClass My { get; set; }

        public override string ToString()
        {
            return string.Format("Id = {0},My = [{1}]", Id, My);
        }
    }
}

2.输出

---------获取属性值---------
113231
113231
Id = 123,My = []
Id = 123,My = []
---------设置属性值---------
设置完成后o的值:Id = 888,My = [Id = 777,My = []]
---------读取属性值【性能测试100万次】---------
表达式树耗时:96.9933ms
反射耗时:244.9508ms
表达式树耗时:94.9008ms
反射耗时:244.1905ms
表达式树耗时:105.9869ms
反射耗时:244.3733ms
表达式树耗时:94.7369ms
反射耗时:248.7527ms
---------设置属性值【性能测试100万次】---------
表达式树耗时:106.949ms
反射耗时:304.0195ms
表达式树耗时:98.9986ms
反射耗时:290.7388ms
表达式树耗时:96.8362ms
反射耗时:294.8361ms
表达式树耗时:96.2894ms
反射耗时:288.5725ms
请按任意键继续. . .

三、函数介绍

方法名称 介绍
T GetPropertyValue(object obj, string name) 获取指定名称的公共属性的值
void SetPropertyValue(object obj, string name, T value) 设置指定名称的公共属性的值
bool TryGetPropertyValue(object obj, string name, out T value) 尝试获取指定名称的公共属性的值
bool TrySetPropertyValue(object obj, string name, T value) 尝试设置指定名称的公共属性的值

四、实现代码

1.获取设置属性类型
/// <summary>
/// 对象操作类
/// </summary>
/// <typeparam name="T">获取或者设置属性的类型</typeparam>
public static class ObjectUtil<T>
{
    private static readonly ConcurrentDictionary<KeyInfo, Func<object, T>> ReadPropertyValueDictionary =
        new ConcurrentDictionary<KeyInfo, Func<object, T>>();

    private static readonly ConcurrentDictionary<KeyInfo, Action<object, T>> WritePropertyValueDictionary =
        new ConcurrentDictionary<KeyInfo, Action<object, T>>();

    /// <summary>
    /// 获取指定名称的公共属性的值
    /// </summary>
    /// <param name="obj">实例对象</param>
    /// <param name="name">属性名称</param>
    /// <returns>值</returns>
    public static T GetPropertyValue(object obj, string name)
    {
        if (obj == null) throw new Exception("传入对象不能为空!");
        var type = obj.GetType();
        var key = new KeyInfo(type, name);
        Func<object, T> getValueAction;
        if (!ReadPropertyValueDictionary.TryGetValue(key, out getValueAction))
        {
            string errorMessage;
            if (CreateReadPropertyFunc(type, name, out getValueAction, out errorMessage))
            {
                getValueAction = ReadPropertyValueDictionary.GetOrAdd(key, getValueAction);
            }
            else
            {
                throw new Exception(errorMessage);
            }
        }

        return getValueAction.Invoke(obj);
    }

    /// <summary>
    /// 尝试获取指定名称的公共属性的值
    /// </summary>
    /// <param name="obj">实例对象</param>
    /// <param name="name">属性名称</param>
    /// <param name="value">属性值</param>
    /// <returns>是否获取成功</returns>
    public static bool TryGetPropertyValue(object obj, string name, out T value)
    {
        if (obj == null)
        {
            value = default(T);
            return false;
        }

        var type = obj.GetType();
        var key = new KeyInfo(type, name);
        Func<object, T> getValueAction;
        if (!ReadPropertyValueDictionary.TryGetValue(key, out getValueAction))
        {
            string errorMessage;
            if (CreateReadPropertyFunc(type, name, out getValueAction, out errorMessage))
            {
                getValueAction = ReadPropertyValueDictionary.GetOrAdd(key, getValueAction);
            }
            else
            {
                value = default(T);
                return false;
            }
        }

        value = getValueAction.Invoke(obj);
        return true;
    }

    /// <summary>
    /// 设置指定名称的公共属性的值
    /// </summary>
    /// <param name="obj">实例对象</param>
    /// <param name="name">属性名称</param>
    /// <param name="value">设置的值</param>
    public static void SetPropertyValue(object obj, string name, T value)
    {
        if (obj == null) throw new Exception("传入对象不能为空!");
        var type = obj.GetType();
        var key = new KeyInfo(type, name);
        Action<object, T> setValueAction;
        if (!WritePropertyValueDictionary.TryGetValue(key, out setValueAction))
        {
            string errorMessage;
            if (CreateWritePropertyFunc(type, name, out setValueAction, out errorMessage))
            {
                setValueAction = WritePropertyValueDictionary.GetOrAdd(key, setValueAction);
            }
            else
            {
                throw new Exception(errorMessage);
            }
        }

        setValueAction.Invoke(obj, value);
    }

    /// <summary>
    /// 尝试设置指定名称的公共属性的值
    /// </summary>
    /// <param name="obj">实例对象</param>
    /// <param name="name">属性名称</param>
    /// <param name="value">设置的值</param>
    public static bool TrySetPropertyValue(object obj, string name, T value)
    {
        if (obj == null) return false;
        var type = obj.GetType();
        var key = new KeyInfo(type, name);
        Action<object, T> setValueAction;
        if (!WritePropertyValueDictionary.TryGetValue(key, out setValueAction))
        {
            string errorMessage;
            if (CreateWritePropertyFunc(type, name, out setValueAction, out errorMessage))
            {
                setValueAction = WritePropertyValueDictionary.GetOrAdd(key, setValueAction);
            }
            else
            {
                return false;
            }
        }

        setValueAction.Invoke(obj, value);
        return true;
    }

    /// <summary>
    /// 创建写入函数
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="name">写入的字段</param>
    /// <param name="setValueAction">写入函数</param>
    /// <param name="errorMessage">异常信息</param>
    /// <returns>是否创建写入函数成功</returns>
    private static bool CreateWritePropertyFunc(Type type, string name, out Action<object, T> setValueAction,
        out string errorMessage)
    {
        var info = type.GetProperty(name);
        if (info == null)
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”的公共属性!", type, name);
            setValueAction = null;
            return false;
        }

        if (info.PropertyType != typeof(T)
            //验证是否支持数值类型的隐式转换
            && !(info.PropertyType.IsValueType && typeof(T).IsValueType &&
                 ImplicitConversionUtil.ImplicitConversion(typeof(T), info.PropertyType))
            //验证是否支持里式转换
            && !info.PropertyType.IsAssignableFrom(typeof(T)))
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”且类型可设置为“{2}”的公共属性!", type, name, typeof(T));
            setValueAction = null;
            return false;
        }

        if (!info.CanWrite)
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”且可写的公共属性!", type, name, typeof(T));
            setValueAction = null;
            return false;
        }

        //生成表达式树
        var oExpression = Expression.Parameter(typeof(object));
        var vExpression = Expression.Parameter(typeof(T));
        var convertExpression = Expression.Convert(oExpression, type);
        var infoExpression = Expression.Property(convertExpression, info);
        var assign = Expression.Assign(infoExpression,
            info.PropertyType == typeof(T)
                ? (Expression) vExpression
                : Expression.Convert(vExpression, info.PropertyType) //转换
        );
        var lambda = Expression.Lambda<Action<object, T>>(assign, oExpression, vExpression);
        setValueAction = lambda.Compile();
        errorMessage = null;
        return true;
    }

    /// <summary>
    /// 创建读取函数
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="name">读取的字段</param>
    /// <param name="getValueFunc">读取函数</param>
    /// <param name="errorMessage">异常信息</param>
    /// <returns>是否创建写入函数成功</returns>
    private static bool CreateReadPropertyFunc(Type type, string name, out Func<object, T> getValueFunc,
        out string errorMessage)
    {
        var info = type.GetProperty(name);
        if (info == null)
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”的公共属性!", type, name);
            getValueFunc = null;
            return false;
        }

        if (info.PropertyType != typeof(T)
            //验证是否支持数值类型的隐式转换
            && !(info.PropertyType.IsValueType && typeof(T).IsValueType &&
                 ImplicitConversionUtil.ImplicitConversion(info.PropertyType, typeof(T)))
            //验证是否支持里式转换
            && !typeof(T).IsAssignableFrom(info.PropertyType))
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”且类型可转换为“{2}”的公共属性!", type, name, typeof(T));
            getValueFunc = null;
            return false;
        }

        if (!info.CanRead)
        {
            errorMessage = string.Format("类型“{0}”中没有名称为“{1}”且类型为“{2}”且可读的公共属性!", type, name, typeof(T));
            getValueFunc = null;
            return false;
        }

        //生成表达式树
        var oExpression = Expression.Parameter(typeof(object));
        var convertExpression = Expression.Convert(oExpression, type);
        Expression infoExpression = Expression.Property(convertExpression, info);
        var lambda = Expression.Lambda<Func<object, T>>(
            info.PropertyType == typeof(T)
                ? infoExpression
                : Expression.Convert(infoExpression, typeof(T)) //转换
            , oExpression);
        getValueFunc = lambda.Compile();
        errorMessage = null;
        return true;
    }

    /// <summary>
    /// [Type,Name]作为Key的数据结构
    /// </summary>
    private struct KeyInfo : IEquatable<KeyInfo>
    {
        public KeyInfo(Type type, string name)
        {
            Type = type;
            Name = name;
        }

        public Type Type { get; private set; }

        public string Name { get; private set; }

        public bool Equals(KeyInfo other)
        {
            return Equals(Type, other.Type) && Name == other.Name;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is KeyInfo)) return false;
            return Equals((KeyInfo) obj);
        }

        public override int GetHashCode()
        {
            unchecked
            {
                return ((Type != null ? Type.GetHashCode() : 0) * 397) ^ (Name != null ? Name.GetHashCode() : 0);
            }
        }
    }
}
2.隐式转换帮助类
/// <summary>
/// 隐式转换帮助类
/// </summary>
public class ImplicitConversionUtil
{
    /// <summary>
    /// 隐式转换表
    /// </summary>
    private static readonly Dictionary<Type, HashSet<Type>> ImplicitConversionDictionary =
        new Dictionary<Type, HashSet<Type>>(10);

    static ImplicitConversionUtil()
    {
        ImplicitConversionDictionary[typeof(sbyte)] = new HashSet<Type>(new[]
        {
            typeof(short),
            typeof(int),
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(byte)] = new HashSet<Type>(new[]
        {
            typeof(short),
            typeof(ushort),
            typeof(int),
            typeof(uint),
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(short)] = new HashSet<Type>(new[]
        {
            typeof(int),
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(ushort)] = new HashSet<Type>(new[]
        {
            typeof(int),
            typeof(uint),
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(int)] = new HashSet<Type>(new[]
        {
            typeof(long),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(uint)] = new HashSet<Type>(new[]
        {
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(long)] = new HashSet<Type>(new[]
        {
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(char)] = new HashSet<Type>(new[]
        {
            typeof(ushort),
            typeof(int),
            typeof(uint),
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
        ImplicitConversionDictionary[typeof(float)] = new HashSet<Type>(new[]
        {
            typeof(double),
        });
        ImplicitConversionDictionary[typeof(ulong)] = new HashSet<Type>(new[]
        {
            typeof(float),
            typeof(double),
            typeof(decimal),
        });
    }

    /// <summary>
    /// 判断是否可用进行隐式转换
    /// </summary>
    /// <param name="fromType">从什么类型</param>
    /// <param name="arriveType">到什么类型</param>
    /// <returns>是否可用进行隐式转换</returns>
    public static bool ImplicitConversion(Type fromType, Type arriveType)
    {
        HashSet<Type> set;
        return ImplicitConversionDictionary.TryGetValue(fromType, out set) && set.Contains(arriveType);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章