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字节能覆盖大多数情况了吧
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 = , BOOL = false, S = "p1", S2 = "p1" };
var p2 = new Person() { INT = , BOOL = false, S = "p1", S2 = "p1" };
string msg = null; stopwatch.Start();
for (int i = ; i < ; 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 = ; i < ; 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);
}
}

耗时是原生的3倍, 考虑到只有记录日志才使用这个, 使用的机会很少, 对性能的损耗可以认为非常小.
end
C#使用反射获取对象变化的情况的更多相关文章
- Java反射获取对象成员属性,getFields()与getDeclaredFields()方法的区别
Java反射获取对象成员属性,getFields()与getDeclaredFields()方法的区别 在工作中遇到一个问题,就是你需要去判断某个字符串是不是对象的某个成员属性名,然后根据判断结果 ...
- Java反射获取对象VO的属性值(通过Getter方法)
有时候,需要动态获取对象的属性值. 比如,给你一个List,要你遍历这个List的对象的属性,而这个List里的对象并不固定.比如,这次User,下次可能是Company. e.g. 这次我需要做一个 ...
- java利用反射获取对象前后修改的内容(用于日志记录)
import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Metho ...
- 第五课 JAVA反射获取对象属性和方法(通过配置文件)
Service1.java package reflection; public class Service1 { public void doService1(){ System.out.print ...
- C#通过反射获取对象属性,打印所有字段属性的值
获取所有字段的值: public void PrintProperties(Object obj) { Type type = obj.GetType(); foreach( PropertyInfo ...
- 第五课 JAVA反射获取对象属性和方法
package com.hero; import java.lang.reflect.Field; public class TestReflction5 { public static void m ...
- 利用反射获取对象中的值等于x的字段
Field[] field = behavior.getClass().getDeclaredFields(); for (int i = 0; i < field.length; i++) { ...
- c#利用反射获取对象属性值
public static string GetObjectPropertyValue<T>(T t, string propertyname){ Type type = type ...
- JAVA使用反射获取对象的所有属性名
public static void main(String[] args) { Field[] fields=BaseSalary.class.getDeclaredFields(); for (i ...
随机推荐
- u盘辨别
1.从基座颜色上区分: USB2.0接口基座一般为黑色或者白色. USB3.0接口基座为蓝色. 2.从U盘插口针脚区分:USB 2.0是4针脚,而USB 3.0采用了9针脚,针脚比USB2.0多. 3 ...
- 闪动效果的实现 (jquery方式和css方式)以及 keyframes和opacity 与ie等各浏览器兼容问题
opacity 是CSS3中:设置元素的不透明度的属性(支持ie8以上) opacity: value|inherit;value用于规定不透明度.从 0.0 (完全透明)到 1.0(完全不透明). ...
- 初识Git->GitHub
这不是一篇教程,这是学习过程的一个记录,初次使用GitHub的小白请移步文章末尾的参考链接, 先了解Git是什么东西 Git的工作流程 操作Git 练习使用 边用边学 #--------------- ...
- gentoo hibernate
首先修改内核: Power management and ACPI options ---> [*] Suspend to RAM and standby [*] Hibernation (ak ...
- 解决由于服务器调用删除或添加字段后CXF客户端未更新导致异常问题org.apache.cxf.interceptor.Fault: Unmarshalling Error: Unexpected element
采用CXF客户端调用Webservice服务,由于服务端时不时会对Webservice服务删除或添加一些字段,而CXF未及时更新客户端代码导致再次调用服务时报异常错误: Interceptor for ...
- element-ui Form表单校验
使用element-ui自带的表单校验,注意几个点: 1.el-form通过rules属性,绑定校验规则 2.el-form-item的prop属性,设置为需要校验的字段名 3.提交后二次校验 sav ...
- 深度学习实践-物体检测-faster-RCNN(原理和部分代码说明) 1.tf.image.resize_and_crop(根据比例取出特征层,进行维度变化) 2.tf.slice(数据切片) 3.x.argsort()(对数据进行排列,返回索引值) 4.np.empty(生成空矩阵) 5.np.meshgrid(生成二维数据) 6.np.where(符合条件的索引) 7.tf.gather取值
1. tf.image.resize_and_crop(net, bbox, 256, [14, 14], name) # 根据bbox的y1,x1,y2,x2获得net中的位置,将其转换为14*1 ...
- mybatis mapper.xml的特殊操作符
select * from test where id<>1; 但是mybatis报错 <> 应该转义 <> select * from test where i ...
- 用spring的@Scheduled实现定时任务
先在spring的配置文件中添加扫描 在applicationContext.xml中添加 <task:annotation-driven/>,我用的是idea有提示功能 选择第一个后会 ...
- leetcode268缺失数字
int missingNumber(int* nums, int numsSize) { ) /; ;i<numsSize;i++){ sum = sum - nums[i]; } return ...