记录日志时, 经常需要描述对象的状态发生了怎样的变化, 以前处理的非常简单粗暴:
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
使用方法:
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); }}
耗时是原生的3倍, 考虑到只有记录日志才使用这个, 使用的机会很少, 对性能的损耗可以认为非常小.
end