比较两个对象的内容是否相等

我有两个相同类型的复杂(即具有string,int,double,List和其他自制数据类型的对象)对象。 我想比较两者的内容,以确保它们是相同的。 注意:该对象没有实现.Equals(我无法控制)并且没有实现IComparable。

是否有通用的方法(reflection?)来比较两个对象的内容?

谢谢!

是否有通用的方法来比较两个对象的内容?

是的,但通常称为IComparable接口。

如果您可以从类中下载并创建一个实现IComparable的子项,那可能是理想的。

我创建了一个类来执行.NET对象的深度比较。 看到:

https://github.com/GregFinzer/Compare-Net-Objects

我的工作解决方案。!

 private bool Compare(object obj1, object obj2) { if (obj1 == null || obj2 == null) { return false; } if (!obj1.GetType().Equals(obj2.GetType())) { return false; } Type type = obj1.GetType(); if (type.IsPrimitive || typeof(string).Equals(type)) { return obj1.Equals(obj2); } if (type.IsArray) { Array first = obj1 as Array; Array second = obj2 as Array; var en = first.GetEnumerator(); int i = 0; while (en.MoveNext()) { if (!Compare(en.Current, second.GetValue(i))) return false; i++; } } else { foreach (PropertyInfo pi in type.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)) { var val = pi.GetValue(obj1); var tval = pi.GetValue(obj2); if (!Compare(val, tval)) return false; } foreach (FieldInfo fi in type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)) { var val = fi.GetValue(obj1); var tval = fi.GetValue(obj2); if (!Compare(val, tval)) return false; } } return true; } 

希望能帮助到你。!

反思就是它,但问题是包含的类型 – 例如,你不能只使用EqualsEqualityComparer ,因为如果它是List等子数据也不方便比较。

你经常需要这样做吗? 你能序列化它们并比较序列化值吗? 这可能是最强大的选择。

GetHashcode适合我。

我在每个类中重写GetHashcode(),并且所有公共相关属性都是X-OR-ed

 override GetHashCode() { return A.GetHashCode() ^ B.GetHashCode ^ C.SafeString().Get.. } 

我遍历所有类,再次X-OR值。 IsModified将先前的HashValue与当前值进行比较。 两个不同的对象确实可以返回相同的HashValue,可能有1到40亿,但是出于很多目的,这对我来说已经足够了。

但是我有一个更好的想法,使用MemoryStream

这是一个扩展:

 public static bool IsBinaryEqualTo(this object obj, object obj1) { using (MemoryStream memStream = new MemoryStream()) { if (obj == null || obj1 == null) { if (obj == null && obj1 == null) return true; else return false; } BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); binaryFormatter.Serialize(memStream, obj); byte[] b1 = memStream.ToArray(); memStream.SetLength(0); binaryFormatter.Serialize(memStream, obj1); byte[] b2 = memStream.ToArray(); if (b1.Length != b2.Length) return false; for (int i = 0; i < b1.Length; i++) { if (b1[i] != b2[i]) return false; } return true; } } 

我刚刚写了我的版本。 此函数使用generics和reflection。 它通过递归自身工作,直到对象中的所有东西都已经比较或找到一个不相等的东西。

  public static bool CompareObjects(T expectInput, T actualInput) { // If T is primitive type. if (typeof(T).IsPrimitive) { if (expectInput.Equals(actualInput)) { return true; } return false; } if (expectInput is IEquatable) { if (expectInput.Equals(actualInput)) { return true; } return false; } if (expectInput is IComparable) { if (((IComparable)expectInput).CompareTo(actualInput) == 0) { return true; } return false; } // If T is implement IEnumerable. if (expectInput is IEnumerable) { var expectEnumerator = ((IEnumerable)expectInput).GetEnumerator(); var actualEnumerator = ((IEnumerable)actualInput).GetEnumerator(); var canGetExpectMember = expectEnumerator.MoveNext(); var canGetActualMember = actualEnumerator.MoveNext(); while (canGetExpectMember && canGetActualMember && true) { var currentType = expectEnumerator.Current.GetType(); object isEqual = typeof(Utils).GetMethod("CompareObjects").MakeGenericMethod(currentType).Invoke(null, new object[] { expectEnumerator.Current, actualEnumerator.Current }); if ((bool)isEqual == false) { return false; } canGetExpectMember = expectEnumerator.MoveNext(); canGetActualMember = actualEnumerator.MoveNext(); } if (canGetExpectMember != canGetActualMember) { return false; } return true; } // If T is class. var properties = typeof(T).GetProperties(); foreach (var property in properties) { var expectValue = typeof(T).GetProperty(property.Name).GetValue(expectInput); var actualValue = typeof(T).GetProperty(property.Name).GetValue(actualInput); if (expectValue == null || actualValue == null) { if (expectValue == null && actualValue == null) { continue; } return false; } object isEqual = typeof(Utils).GetMethod("CompareObjects").MakeGenericMethod(property.PropertyType).Invoke(null, new object[] { expectValue, actualValue }); if ((bool)isEqual == false) { return false; } } return true; } 

将对象序列化为XML字符串,您最终可以在序列化的2个对象之间进行字符串比较…

 private string Serialize(T value) { if (value == null) { return string.Empty; } try { XmlSerializer xmlserializer = new XmlSerializer(typeof(T)); StringWriter stringWriter = new StringWriter(); XmlWriter writer = XmlWriter.Create(stringWriter); xmlserializer.Serialize(writer, value); string serializeXml = stringWriter.ToString(); writer.Close(); return serializeXml; } catch (Exception ex) { return string.Empty; } } } 

对于我的上一个项目,我对一些function做了很好的深入研究。

  public class ObjektHelper { ///  /// Compairs two Objects and gives back true if they are equal ///  ///  /// Object 1 /// Object 2 /// If the list is not mepty, only the field within equal names are compaired. /// If you want not compair some fields enter their name in this list. ///  public static bool DeepCompare(T obj1, T obj2, string[] consideredFieldNames, params string[] notConsideredFieldNames) { var errorList = new List(); if (notConsideredFieldNames == null) notConsideredFieldNames = new[] {""}; DeepCompare(obj1, obj2, errorList, consideredFieldNames, notConsideredFieldNames, false); return errorList.Count <= 0; } ///  /// Compairs two Objects and gives an error list back. ///  ///  ///  ///  /// The error list gives back the names of the fields that are not equal. /// If the list is not mepty, only the field within equal names are compaired. /// If you want not compair some fields enter their name in this list. /// If the value is false, the method end at the first error. public static void DeepCompare(T obj1, T obj2, List errorList, string[] consideredFieldNames, string[] notConsideredFieldNames, bool endWithErrors) { if (errorList == null) throw new Exception("errorListliste ist NULL"); if (Equals(obj1, default(T)) && Equals(obj2, default(T))) return; if (Equals(obj1, default(T)) || Equals(obj2, default(T))) { errorList.Add("One of the object are null!"); return; } if (!endWithErrors && errorList != null && errorList.Count > 0) throw new Exception("ErrorListliste is not empty"); var type1 = obj1.GetType(); var type2 = obj2.GetType(); var propertyInfos1 = type1.GetProperties(); var propertyInfos2 = type2.GetProperties(); // To use the access via index, the list have to be ordered! var propertyInfoOrdered1 = propertyInfos1.OrderBy(p => p.Name).ToArray(); var propertyInfoOrdered2 = propertyInfos2.OrderBy(p => p.Name).ToArray(); if (type1 != type2) errorList.AddRange(new List {type1, type2}); else { for (var i = 0; i < propertyInfos1.Length; i++) { var t1 = propertyInfoOrdered1[i].PropertyType; var t2 = propertyInfoOrdered2[i].PropertyType; if (t1 != t2) { errorList.AddRange(new List {type1, type2}); continue; } var name1 = propertyInfoOrdered1[i].Name; var name2 = propertyInfoOrdered2[i].Name; // Use the next 4 lines to find a bug //if (name1 == "Enter name of field with the bug") // Console.WriteLine(name1); //if (name2 == "Enter name of field1 with the bug" || name2 == "Enter name of field2 with the bug") // Console.WriteLine(name2); if (consideredFieldNames != null && !consideredFieldNames.Contains(name1)) continue; if (notConsideredFieldNames != null && notConsideredFieldNames.Contains(name1)) continue; var value1 = propertyInfoOrdered1[i].GetValue(obj1, null); var value2 = propertyInfoOrdered2[i].GetValue(obj2, null); // check Attributes var guiName1 = (propertyInfoOrdered1[i].GetCustomAttributes().FirstOrDefault(a => a.GetType() == typeof(GuiNameofModelAttribute)) as GuiNameofModelAttribute)?.GuiName; var guiName2 = (propertyInfoOrdered2[i].GetCustomAttributes().FirstOrDefault(a => a.GetType() == typeof(GuiNameofModelAttribute)) as GuiNameofModelAttribute)?.GuiName; // create errorListrange var temperrorListRange = new List(); if (guiName1 != null && guiName2 != null) temperrorListRange.AddRange(new List { guiName1, guiName2 }); else temperrorListRange.AddRange(new List { propertyInfoOrdered1[i], propertyInfoOrdered2[i] }); // both fields are null = OK if ((value1 == null && value2 == null) || (value1 is Guid && value2 is Guid)) continue; // one of the fields is null = errorList if (value1 == null || value2 == null) errorList.AddRange(temperrorListRange); // Value types, Enum and String compair else if (t1.BaseType == typeof (ValueType) || t1.BaseType == typeof (Enum) || t1 == typeof (string)) { if (!value1.Equals(value2)) errorList.AddRange(temperrorListRange); } // List, array, generic lists, collection and bindinglist compair else if (t1 == typeof (Array) || (t1.IsGenericType && (t1.GetGenericTypeDefinition() == typeof (List<>) || t1.GetGenericTypeDefinition() == typeof (IList<>) || t1.GetGenericTypeDefinition() == typeof (Collection<>) || t1.GetGenericTypeDefinition() == typeof (ICollection<>) || t1.GetGenericTypeDefinition() == typeof (ObservableCollection<>) || t1.GetGenericTypeDefinition() == typeof (BindingList<>) || t1.GetGenericTypeDefinition() == typeof (BindingList<>) ))) DeepListCompare(value1 as IEnumerable, value2 as IEnumerable, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors); // Clas compair else if (t1.IsClass || t1.IsAnsiClass) DeepCompare(value1, value2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors); else throw new NotImplementedException(); if (!endWithErrors && errorList.Count > 0) break; } } } // End DeepCompare ///  /// Compairs two lists and gives back true if they are equal. ///  ///  /// Generic list 1 /// Generic List 2 ///  public static bool DeepListCompare(T tlist1, T tlist2) { var errorList = new List(); DeepCompare(tlist1, tlist2, errorList, null, null, false); return errorList.Count <= 0; } ///  /// Compairs two lists and gives backthe error list. ///  ///  /// Generic list 1 /// Generic list 2 /// The error list gives back the names of the fields that are not equal. /// If the list is not mepty, only the field within equal names are compaired. /// If you want not compair some fields enter their name in this list. /// If the value is false, the method end at the first error. public static void DeepListCompare(T tlist1, T tlist2, List errorList, string[] consideredFieldNames, string[] notConsideredFieldNames, bool endWithErrors) where T : IEnumerable { if (errorList == null) throw new Exception("errorListliste ist NULL"); if (!endWithErrors && errorList.Count > 0) throw new Exception("errorListliste ist nicht Leer"); if (Equals(tlist1, null) || Equals(tlist2, null)) { errorList.AddRange(new List {tlist1, tlist2}); return; } var type1 = tlist1.GetType(); var type2 = tlist2.GetType(); var propertyInfos1 = type1.GetProperties(); var propertyInfos2 = type2.GetProperties(); // To use the access via index, the list have to be ordered! var propertyInfoOrdered1 = propertyInfos1.OrderBy(p => p.Name).ToArray(); var propertyInfoOrdered2 = propertyInfos2.OrderBy(p => p.Name).ToArray(); for (var i = 0; i < propertyInfos1.Length; i++) { var t1 = propertyInfoOrdered1[i].PropertyType; var t2 = propertyInfoOrdered2[i].PropertyType; if (t1 != t2) errorList.AddRange(new List {t1, t2}); else { // Kick out index if (propertyInfoOrdered1[i].GetIndexParameters().Length != 0) { continue; } // Get value var value1 = propertyInfoOrdered1[i].GetValue(tlist1, null) as IEnumerable; var value2 = propertyInfoOrdered2[i].GetValue(tlist2, null) as IEnumerable; if (value1 == null || value2 == null) continue; // Only run through real lists. if (t1 == typeof (Array) || t1.IsGenericType && t1.GetGenericTypeDefinition() == typeof (List<>) || t1.IsGenericType && t1.GetGenericTypeDefinition() == typeof (Collection<>)) { // cast var objectList1 = value1.ToList(); var objectList2 = value2.ToList(); if (objectList1.Count == 0 && objectList1.Count == 0) { //errorList.AddRange(new List { objectList1, objectList1 }); continue; } foreach (var item1 in objectList1) { foreach (var item2 in objectList2) { var temperrorListCount = errorList.Count; DeepCompare(item1, item2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors); if (temperrorListCount != errorList.Count) continue; objectList2.Remove(item2); break; } if (!endWithErrors && errorList.Count > 0) break; } } else DeepCompare(value1, value2, errorList, consideredFieldNames, notConsideredFieldNames, endWithErrors); } if (!endWithErrors && errorList.Count > 0) break; } } // End DeepListCompare } // end class ObjectHelper [AttributeUsage(AttributeTargets.All)] public class GuiNameofModelAttribute : Attribute { public readonly string GuiName; public GuiNameofModelAttribute(string guiName) { GuiName = guiName; } } 

您可以简单地在另一个类中编写实用程序方法来进行比较。 但是,假设所讨论的类的属性是公开可访问的。 是吗?

感谢MemoryStream方法,Marc。 我确信当我在参数中看到“this”时出现了错误,但令人惊讶的是编译器实际上让你这样做,是吧? 我做了一个小改动,然后选择覆盖到Equals()。 Kudos也使用长度和数组比较而不是SequenceEquals()。 花一点时间写,但根据http://www.dotnetperls.com/sequenceequal ,性能要好得多。

 public override bool Equals(object obj) { // If comparison object is null or is a different type, no further comparisons are necessary... if (obj == null || GetType() != obj.GetType()) { return false; } // Compare objects using byte arrays... using (MemoryStream memStream = new MemoryStream()) { BinaryFormatter binaryFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.Clone)); // Get byte array of "this" object... binaryFormatter.Serialize(memStream, this); byte[] b1 = memStream.ToArray(); // Get byte array of object to be compared... memStream.SetLength(0); binaryFormatter.Serialize(memStream, obj); byte[] b2 = memStream.ToArray(); // Compare array sizes. If equal, no further comparisons are necessary... if (b1.Length != b2.Length) return false; // If sizes are equal, compare each byte while inequality is not found... for (int i = 0; i < b1.Length; i++) { if (b1[i] != b2[i]) return false; } } return true; } 

我发现最快最简单的方法是使用MessagePack序列化两个对象,然后比较字节数组。

 public static bool DeepEquals(object o1, object o2) { var b1 = MessagePackSerializer.Serialize(o1, ContractlessStandardResolver.Instance); var b2 = MessagePackSerializer.Serialize(o2, ContractlessStandardResolver.Instance); return b1.SequenceEqual(b2); } 

好吧,你可以编写一些逻辑来比较两个对象的所有属性。 当它是具有复杂子类型的对象图时,这会变得复杂,因此您需要确定接近的程度。

你需要一个别的比较方法; 在C ++中你可以编写一个全局函数,但我不认为c#允许这样,就像Java没有那样。

我要做的是写一个实现iComparable的类,并且有一个ctor,它接受你想要的类的对象,并包含你的Equals函数。 设置它所以它保留的是对原始对象的引用,以保存mallocations。
然后你就可以写了

Foo(A).Equals(new Foo(B))

您可以改为从提供的类inheritance,但这意味着需要创建和跟踪这些内容。