zoukankan      html  css  js  c++  java
  • C#使用反射获取对象变化的情况

      记录日志时, 经常需要描述对象的状态发生了怎样的变化, 以前处理的非常简单粗暴:

      a. 重写class的ToString()方法, 将重要的属性都输出来

      b. 记录日志时:  谁谁谁  由  变更前实例.ToString()   变成   变更后实例.ToString()

      但输出的日志总是太长了, 翻看日志时想找到差异也非常麻烦, 所以想输出为:  谁谁谁的哪个属性由  aaa 变成了 bbb

      手写代码一个一个的比较字段然后输出这样的日志信息, 是不敢想象的事情. 本来想参考Dapper使用 System.Reflection.Emit 发射 来提高运行效率, 但实在没有功夫研究.Net Framework的中间语言, 所以准备用 Attribute特性 和 反射 来实现

    /// <summary>
    /// 要比较的字段或属性, 目前只支持C#基本类型, 比如 int, bool, string等, 你自己写的class或者struct 需要重写 ToString()、Equals(), 按理说如果重写了Equals(), 那也需要重写GetHashCode(), 但确实没有用到GetHashCode(), 所以可以忽略Warning不重写GetHashCode();
    /// </summary>
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
    public class ComparePropertyFieldAttribute : Attribute
    {
        /// <summary>
        /// 属性或字段的别名
        /// </summary>
        public string PropertyName { get; private set; }
    
        /// <summary>
        /// 要比较的字段或属性
        /// </summary>
        public ComparePropertyFieldAttribute()
        { }
    
        /// <summary>
        /// 要比较的字段或属性
        /// </summary>
        /// <param name="propertyName">属性或字段的别名</param>
        public ComparePropertyFieldAttribute(string propertyName)
        {
            PropertyName = propertyName;
        }
    
        // 缓存反射的结果,  Tuple<object, ComparePropertyAttribute> 中第一个参数之所以用object 是因为要保存 PropertyInfo 和 FieldInfo
        private static Dictionary<Type, Tuple<object, ComparePropertyFieldAttribute>[]> dict = new Dictionary<Type, Tuple<object, ComparePropertyFieldAttribute>[]>();
    
        /// <summary>
        /// 只对带有ComparePropertyAttribute的属性和字段进行比较
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <param name="differenceMsg">不相同的字段或属性 的字符串说明</param>
        /// <returns>两者相同时, true; 两者不相同时, false</returns>
        public static bool CompareDifference<T>(T from, T to, out string differenceMsg)
        {
            var type = typeof(T);
            lock (dict)
            {
                if (!dict.ContainsKey(type))
                {
                    var list = new List<Tuple<object, ComparePropertyFieldAttribute>>();
                    // 获取带ComparePropertyAttribute的属性
                    var properties = type.GetProperties();
                    foreach (var property in properties)
                    {
                        var comparePropertyAttribute = (ComparePropertyFieldAttribute)property.GetCustomAttributes(typeof(ComparePropertyFieldAttribute), false).FirstOrDefault();
                        if (comparePropertyAttribute != null)
                        {
                            list.Add(Tuple.Create<object, ComparePropertyFieldAttribute>(property, comparePropertyAttribute));
                        }
                    }
                    // 获取带ComparePropertyAttribute字段
                    var fields = type.GetFields();
                    foreach (var field in fields)
                    {
                        var comparePropertyAttribute = (ComparePropertyFieldAttribute)field.GetCustomAttributes(typeof(ComparePropertyFieldAttribute), false).FirstOrDefault();
                        if (comparePropertyAttribute != null)
                        {
                            list.Add(Tuple.Create<object, ComparePropertyFieldAttribute>(field, comparePropertyAttribute));
                        }
                    }
    
                    dict.Add(type, list.ToArray());
                }
            }
    
            var sb = new StringBuilder(200); //估计200字节能覆盖大多数情况了吧
            var tupleArray = dict[type];
            foreach (var tuple in tupleArray)
            {
                object v1 = null, v2 = null;
                if (tuple.Item1 is System.Reflection.PropertyInfo)
                {
                    if (from != null)
                    {
                        v1 = ((System.Reflection.PropertyInfo)tuple.Item1).GetValue(from, null);
                    }
                    if (to != null)
                    {
                        v2 = ((System.Reflection.PropertyInfo)tuple.Item1).GetValue(to, null);
                    }
                    if (!object.Equals(v1, v2))
                    {
                        sb.AppendFormat("{0}从 {1} 变成 {2}; ", tuple.Item2.PropertyName ?? ((System.Reflection.PropertyInfo)tuple.Item1).Name, v1 ?? "null", v2 ?? "null");
                    }
                }
                else if (tuple.Item1 is System.Reflection.FieldInfo)
                {
                    if (from != null)
                    {
                        v1 = ((System.Reflection.FieldInfo)tuple.Item1).GetValue(from);
                    }
                    if (to != null)
                    {
                        v2 = ((System.Reflection.FieldInfo)tuple.Item1).GetValue(to);
                    }
                    if (!object.Equals(v1, v2))
                    {
                        sb.AppendFormat("{0}从 {1} 变成 {2}; ", tuple.Item2.PropertyName ?? ((System.Reflection.FieldInfo)tuple.Item1).Name, v1 ?? "null", v2 ?? "null");
                    }
                }
            }
    
            differenceMsg = sb.ToString();
            return differenceMsg == "";
        }
    }
    ComparePropertyFieldAttribute

      使用方法:

      1. 将重要字段或属性加上 [ComparePropertyField] 特性, 目前只支持C#基本类型, 比如 int, bool, string等, 你自己写的class或者struct 需要重写 ToString()、Equals(), 按理说如果重写了Equals(), 那也需要重写GetHashCode(), 但确实没有用到GetHashCode(), 所以可以忽略Warning不重写GetHashCode()

      2. 使用ComparePropertyFieldAttribute.CompareDifference 比较变更前后的实例即可

      具体可参考下面的示例

    class Program
    {
        static void Main(string[] args)
        {
            // 请用Debug测试, Release会优化掉一些代码导致测试不准确
            System.Diagnostics.Stopwatch stopwatch = new Stopwatch();
            var p1 = new Person() { INT = 1, BOOL = false, S = "p1", S2 = "p1" };
            var p2 = new Person() { INT = 3, BOOL = false, S = "p1", S2 = "p1" };
            string msg = null;
    
            stopwatch.Start();
            for (int i = 0; i < 10000000; i++)
            {
                if (!p1.Equals(p2))
                {
                    msg = string.Format("{0} 变成 {1}", p1.ToString(), p2.ToString());
                }
            }
            stopwatch.Stop();
            Console.WriteLine("原生比较结果: " + msg);
            Console.WriteLine("原生比较耗时: " + stopwatch.Elapsed);
    
    
            stopwatch.Start();
            for (int i = 0; i < 10000000; i++)
            {
                var result = ComparePropertyFieldAttribute.CompareDifference<Person>(p1, p2, out msg);
            }
            stopwatch.Stop();
            Console.WriteLine("ComparePropertyAttribute比较结果: " + msg);
            Console.WriteLine("ComparePropertyAttribute比较: " + stopwatch.Elapsed);
    
    
            Console.ReadLine();
        }
    }
    
    
    public class Person
    {
        [ComparePropertyField]
        public int INT { get; set; }
    
        [ComparePropertyFieldAttribute("布尔")]
        public bool BOOL { get; set; }
    
        [ComparePropertyFieldAttribute("字符串")]
        public string S { get; set; }
    
        [ComparePropertyFieldAttribute("S22222")]
        public string S2;
    
        public override bool Equals(object obj)
        {
            var another = obj as Person;
            if (another==null)
            {
                return false;
            }
            return this.INT == another.INT &&
                this.BOOL == another.BOOL &&
                this.S == another.S &&
                this.S2 == another.S2;
        }
    
        public override string ToString()
        {
            return string.Format("i={0}, 布尔={1}, 字符串={2}, S22222={3}", INT, BOOL, S, S2);
        }
    }
    View Code

     

     耗时是原生的3倍, 考虑到只有记录日志才使用这个, 使用的机会很少, 对性能的损耗可以认为非常小.

      end

      

  • 相关阅读:
    【排序】冒泡排序,C++实现
    【排序】选择排序,C++实现
    【排序】插入排序,C++实现
    【集成学习】 lightgbm原理
    leetcode1310
    leetcode1309
    leetcode1300
    leetcode1302
    leetcode1299
    leetcode1306
  • 原文地址:https://www.cnblogs.com/zhouandke/p/8679448.html
Copyright © 2011-2022 走看看