博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#使用反射获取对象变化的情况
阅读量:5240 次
发布时间:2019-06-14

本文共 5806 字,大约阅读时间需要 19 分钟。

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

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

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

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

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

/// /// 要比较的字段或属性, 目前只支持C#基本类型, 比如 int, bool, string等, 你自己写的class或者struct 需要重写 ToString()、Equals(), 按理说如果重写了Equals(), 那也需要重写GetHashCode(), 但确实没有用到GetHashCode(), 所以可以忽略Warning不重写GetHashCode();/// [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]public class ComparePropertyFieldAttribute : Attribute{    ///     /// 属性或字段的别名    ///     public string PropertyName { get; private set; }    ///     /// 要比较的字段或属性    ///     public ComparePropertyFieldAttribute()    { }    ///     /// 要比较的字段或属性    ///     /// 属性或字段的别名    public ComparePropertyFieldAttribute(string propertyName)    {        PropertyName = propertyName;    }    // 缓存反射的结果,  Tuple
中第一个参数之所以用object 是因为要保存 PropertyInfo 和 FieldInfo private static Dictionary
[]> dict = new Dictionary
[]>(); ///
/// 只对带有ComparePropertyAttribute的属性和字段进行比较 /// ///
///
///
///
不相同的字段或属性 的字符串说明 ///
两者相同时, true; 两者不相同时, false
public static bool CompareDifference
(T from, T to, out string differenceMsg) { var type = typeof(T); lock (dict) { if (!dict.ContainsKey(type)) { var list = new List
>(); // 获取带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
(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
(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
(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

 

 

 

  

转载于:https://www.cnblogs.com/zhouandke/p/8679448.html

你可能感兴趣的文章
转:apache 的mod-status
查看>>
转:基于InfluxDB&Grafana的JMeter实时性能测试数据的监控和展示
查看>>
结对编程博客
查看>>
Kendo MVVM 数据绑定(四) Disabled/Enabled
查看>>
python学习笔记3-列表
查看>>
程序的静态链接,动态链接和装载 (补充)
查看>>
关于本博客说明
查看>>
C++11 生产者消费者
查看>>
IO multiplexing 与 非阻塞网络编程
查看>>
hdu4105  Electric wave
查看>>
基于内容的图片检索CBIR(Content Based Image Retrieval)简介
查看>>
线程androidAndroid ConditionVariable的用法
查看>>
程序电脑VS2008 应用程序配置不正确,未能启动该应用程序。重新安装程序可以修复此问题。解决方法...
查看>>
设置类UIColor使用colorWithRed定义颜色
查看>>
文件语音识别Google语音识别学习札记 - Windows PC机上测试语音识别Strut2教程-java教程...
查看>>
μC/OS-III---I笔记13---中断管理
查看>>
:after,:before,content
查看>>
FTTB FTTC FTTH FTTO FSA
查看>>
OpenAI Gym
查看>>
stap-prep 需要安装那些内核符号
查看>>