C# C语言中两个对象之间的差异#
我想知道如何才能发现同一类的两个对象之间的差异。因此,如果我有一个Person类,唯一的区别是Age,它将返回不同的字段C# C语言中两个对象之间的差异#,c#,reflection,comparison,C#,Reflection,Comparison,我想知道如何才能发现同一类的两个对象之间的差异。因此,如果我有一个Person类,唯一的区别是Age,它将返回不同的字段 感谢这不是C#(或.NET)真正直接支持的东西,但是您可以手动实现特定类型的东西,或者编写使用反射区分任意对象的代码 如果选择后者,则必须确定要深入对象图的深度,以确定两个实例是否相同,以及如何比较某些基本类型是否相等(例如,双精度) 编写一个基于反射的差分算法比一开始看起来要困难得多——就我个人而言,我会直接为您需要它的类型(或在助手类中)实现此功能。您需要递归地遍历整个对
感谢这不是C#(或.NET)真正直接支持的东西,但是您可以手动实现特定类型的东西,或者编写使用反射区分任意对象的代码 如果选择后者,则必须确定要深入对象图的深度,以确定两个实例是否相同,以及如何比较某些基本类型是否相等(例如,双精度)
编写一个基于反射的差分算法比一开始看起来要困难得多——就我个人而言,我会直接为您需要它的类型(或在助手类中)实现此功能。您需要递归地遍历整个对象图上的所有私有和公共属性和字段。使用哈希集跟踪已检查的对象,这样就不会返回重复的结果或导致堆栈溢出
如果属性类型为IComparable,则可以将该属性的值强制转换为IComparable,并使用IComparable.CompareTo。如果没有,您将不得不递归地调用子对象上的your differential方法。这实际上取决于您想要比较实体的深度,但是使用反射的想法在这里是最好的。代码应该是这样的:
public class Pair
{
public object Value1
{
get;
set;
}
public object Value2
{
get;
set;
}
}
//somewhere in another class
public Dictionary<string, Pair> Compare<T>(T object1, T object2)
{
var props = typeof(T).GetProperties().Where(pi => pi.CanRead); //this will return only public readable properties. Modify if you need something different
Dictionary<string, Pair> result = new Dictionary<string, Pair>();
foreach (var prop in props)
{
var val1 = prop.GetValue(object1, null); //indexing properties are ignored here
var val2 = prop.GetValue(object2, null);
if (val1 != val2) //maybe some more sophisticated compare algorithm here, using IComparable, nested objects analysis etc.
{
result[prop.Name] = new Pair { Value1 = val1, Value2 = val2 };
}
}
return result;
}
公共类对
{
公共物品价值1
{
得到;
设置
}
公共物品价值2
{
得到;
设置
}
}
//在另一个班级的某个地方
公共字典比较(T object1,T object2)
{
var props=typeof(T).GetProperties().Where(pi=>pi.CanRead);//这将只返回公共可读的属性。如果需要其他内容,请进行修改
字典结果=新字典();
foreach(道具中的var道具)
{
var val1=prop.GetValue(object1,null);//此处忽略索引属性
var val2=prop.GetValue(object2,null);
if(val1!=val2)//这里可能有更复杂的比较算法,使用IComparable、嵌套对象分析等。
{
结果[prop.Name]=新对{Value1=val1,Value2=val2};
}
}
返回结果;
}
如果您需要深入处理嵌套对象,那么正如前面所说的,您将需要一些哈希表来记住已经处理过的对象并防止它们再次被处理。希望这有帮助 像这样的东西怎么样 这将获得两个对象之间不同的属性名称列表。我不认为这就是你所寻求的解决方案,但我认为这是一个不错的开始
Foo foo1 = new Foo { Prop1 = "One", Prop2 = "Two"};
Foo foo2 = new Foo { Prop1 = "One", Prop2 = "Three" };
Type fooType = typeof (Foo);
PropertyInfo[] properties = fooType.GetProperties();
var diffs = from property in properties
let first = foo1
let second = foo2
where property.GetValue(first, null) != property.GetValue(second, null)
select property;
在我的示例中,这将返回“Prop2”,因为这是对象之间值不同的属性
编辑:当然,这假设对象中的任何复杂类型都实现了与预期相同的比较。如果没有,您需要深入对象图并按照其他人的建议进行嵌套比较以下是一些我在调试时使用的简单代码:
//This structure represents the comparison of one member of an object to the corresponding member of another object.
public struct MemberComparison
{
public readonly MemberInfo Member; //Which member this Comparison compares
public readonly object Value1, Value2;//The values of each object's respective member
public MemberComparison(MemberInfo member, object value1, object value2)
{
Member = member;
Value1 = value1;
Value2 = value2;
}
public override string ToString()
{
return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString();
}
}
//This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects.
public List<MemberComparison> ReflectiveCompare<T>(T x, T y)
{
List<MemberComparison> list = new List<MemberComparison>();//The list to be returned
foreach (MemberInfo m in typeof(T).GetMembers(BindingFlags.NonPublic | BindingFlags.Instance))
//Only look at fields and properties.
//This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare
if (m.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)m;
var xValue = field.GetValue(x);
var yValue = field.GetValue(y);
if (!object.Equals(xValue, yValue))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'.
list.Add(new MemberComparison(field, yValue, xValue));
}
else if (m.MemberType == MemberTypes.Property)
{
var prop = (PropertyInfo)m;
if (prop.CanRead && prop.GetGetMethod().GetParameters().Length == 0)
{
var xValue = prop.GetValue(x, null);
var yValue = prop.GetValue(y, null);
if (!object.Equals(xValue, yValue))
list.Add(new MemberComparison(prop, xValue, yValue));
}
else//Ignore properties that aren't readable or are indexers
continue;
}
return list;
}
//此结构表示对象的一个成员与另一个对象的相应成员之间的比较。
公共结构成员比较
{
public readonly MemberInfo Member;//比较哪个成员
public readonly object Value1,Value2;//每个对象各自成员的值
公共成员比较(MemberInfo成员、对象值1、对象值2)
{
成员=成员;
Value1=Value1;
Value2=Value2;
}
公共重写字符串ToString()
{
返回成员.Name+“:”+Value1.ToString()+(Value1.Equals(Value2)?“==”:“!=”+Value2.ToString();
}
}
//此方法可用于获取MemberComparison值列表,这些值表示两个对象之间不同的字段和/或属性。
公共列表反射率比较(T x,T y)
{
List List=new List();//要返回的列表
foreach(类型为(T).GetMembers的MemberInfo m(BindingFlags.NonPublic | BindingFlags.Instance))
//只查看字段和属性。
//可以将其更改为包含方法,但必须获取传递给要比较的方法的值
if(m.MemberType==MemberTypes.Field)
{
FieldInfo字段=(FieldInfo)m;
var xValue=field.GetValue(x);
var yValue=field.GetValue(y);
if(!object.Equals(xValue,yValue))//如果在“x”上定义的成员的值不等于在“y”上定义的成员的值,则向列表中添加新的比较。
添加(新成员比较(字段、yValue、xValue));
}
else if(m.MemberType==MemberTypes.Property)
{
var prop=(PropertyInfo)m;
if(prop.CanRead&&prop.getMethod().GetParameters().Length==0)
{
var xValue=prop.GetValue(x,null);
var yValue=prop.GetValue(y,null);
如果(!object.Equals(xValue,yValue))
添加(新成员比较(prop、xValue、yValue));
}
else//忽略不可读或是索引器的属性
继续;
}
退货清单;
}
要使用它,您的代码可能如下所示:
public static void Main()
{
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
// ...Code that changes object1 and/or object2...
//Here's your answer: a list of what's different between the 2 objects, and each of their different values.
//No type parameters are needed here- typeof(MyObject) is implied by the coincident types of both parameters.
List<MemberComparison> changes = ReflectiveCompare(object1, object2);
}
publicstaticvoidmain()
{
MyObject object1=新的MyObject();
MyObject object2=新的MyObject();
//…更改object1和/或object2的代码。。。
//以下是您的答案:列出两个对象之间的区别
public struct MemberComparison
{
public readonly System.Reflection.MemberInfo Member; //Which member this Comparison compares
public readonly object Value1, Value2;//The values of each object's respective member
public readonly Exception Value1Exception, Value2Exception;
public MemberComparison(System.Reflection.MemberInfo member, object value1, object value2, Exception value1Exception = null, Exception value2Exception = null)
{
Member = member;
Value1 = value1;
Value2 = value2;
Value1Exception = value1Exception;
Value2Exception = value2Exception;
}
public override string ToString()
{
if (Value1Exception != null && Value2Exception != null)
{
if (Value1Exception.GetType().Equals(Value2Exception.GetType()))
{
return Member.Name + ": Exception in both, same exception type of type "+Value1Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message+", differences in type value: " + string.Join("\n", ReflectiveCompare(Value1Exception, Value2Exception).ToArray());
}
else if (!Value1Exception.GetType().Equals(Value2Exception.GetType()))
{
return Member.Name + ": Exception in both, different exception type: " + Value1Exception.GetType().Name + " : " + Value2Exception.GetType().Name+", message in first exception: " +Value1Exception.Message+", message in second exception: "+Value2Exception.Message;
}
}
else if (Value1Exception != null && Value2Exception == null)
{
return Member.Name + ": "+ Value2.ToString()+" Exception in first of type " + Value1Exception.GetType().Name+", message is: "+Value1Exception.Message;
}
else if (Value1Exception == null && Value2Exception != null)
{
return Member.Name + ": "+ Value1.ToString()+" Exception in second of type " + Value2Exception.GetType().Name+", message is: "+Value2Exception.Message;
}
return Member.Name + ": " + Value1.ToString() + (Value1.Equals(Value2) ? " == " : " != ") + Value2.ToString();
}
}
public static bool isCollection(object obj)
{
return obj.GetType().GetInterfaces()
.Any(iface => (iface.GetType() == typeof(ICollection) || iface.GetType() == typeof(IEnumerable) || iface.GetType() == typeof(IList)) || (iface.IsGenericTypeDefinition && (iface.GetGenericTypeDefinition() == typeof(ICollection<>) || iface.GetGenericTypeDefinition() == typeof(IEnumerable<>) || iface.GetGenericTypeDefinition() == typeof(IList<>))));
}
//This method can be used to get a list of MemberComparison values that represent the fields and/or properties that differ between the two objects.
public static List<MemberComparison> ReflectiveCompare<T>(T x, T y)
{
List<MemberComparison> list = new List<MemberComparison>();//The list to be returned
var memb = typeof(T).GetMembers();
foreach (System.Reflection.MemberInfo m in memb)
//Only look at fields and properties.
//This could be changed to include methods, but you'd have to get values to pass to the methods you want to compare
if (m.MemberType == System.Reflection.MemberTypes.Field)
{
System.Reflection.FieldInfo field = (System.Reflection.FieldInfo)m;
Exception excep1 = null;
Exception excep2 = null;
object xValue = null;
object yValue = null;
try
{
xValue = field.GetValue(x);
}
catch (Exception e)
{
excep1 = e;
}
try
{
yValue = field.GetValue(y);
}
catch (Exception e)
{
excep2 = e;
}
if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); }
else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); }
else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(field, yValue, xValue, excep1, excep2)); }
else if ((xValue == null && yValue == null)) { continue; }
else if (xValue == null || yValue == null) list.Add(new MemberComparison(field, yValue, xValue));
else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue, yValue).Count > 0)))//Add a new comparison to the list if the value of the member defined on 'x' isn't equal to the value of the member defined on 'y'.
list.Add(new MemberComparison(field, yValue, xValue));
}
else if (m.MemberType == System.Reflection.MemberTypes.Property)
{
var prop = (System.Reflection.PropertyInfo)m;
if (prop.CanRead && !(prop.GetGetMethod() == null || prop.GetGetMethod().GetParameters() == null) && prop.GetGetMethod().GetParameters().Length == 0)
{
Exception excep1 = null;
Exception excep2 = null;
object xValue = null;
object yValue = null;
try
{
xValue = prop.GetValue(x, null);
}
catch (Exception e)
{
excep1 = e;
}
try
{
yValue = prop.GetValue(y, null);
}
catch (Exception e)
{
excep2 = e;
}
if ((excep1 != null && excep2 == null) || (excep1 == null && excep2 != null)) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); }
else if (excep1 != null && excep2 != null && !excep1.GetType().Equals(excep2.GetType())) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); }
else if (excep1 != null && excep2 != null && excep1.GetType().Equals(excep2.GetType()) && ReflectiveCompare(excep1, excep2).Count > 0) { list.Add(new MemberComparison(prop, yValue, xValue, excep1, excep2)); }
else if ((xValue == null && yValue == null)) { continue; }
else if (xValue == null || yValue == null) list.Add(new MemberComparison(prop, yValue, xValue));
else if (!xValue.Equals(yValue) && ((!isCollection(xValue) && !isCollection(yValue)) || (isCollection(xValue) && !isCollection(yValue)) || (!isCollection(xValue) && isCollection(yValue)) || (isCollection(xValue) && isCollection(yValue) && ReflectiveCompare(xValue,yValue).Count > 0)))// || (isCollection(xValue) && isCollection(yValue) && ((IEnumerable<T>)xValue).OrderBy(i => i).SequenceEqual(xValue.OrderBy(i => i))) )))
list.Add(new MemberComparison(prop, xValue, yValue));
}
else//Ignore properties that aren't readable or are indexers
continue;
}
return list;
}