C# 比较两个复杂对象的最佳方法
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10454519/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Best way to compare two complex objects
提问by Developer
I have two complex objects like Object1and Object2. They have around 5 levels of child objects.
我有两个复杂的对象,比如Object1和Object2。他们有大约 5 个级别的子对象。
I need the fastest method to say if they are same or not.
我需要最快的方法来判断它们是否相同。
How could this be done in C# 4.0?
如何在 C# 4.0 中做到这一点?
采纳答案by Douglas
Implement IEquatable<T>(typically in conjunction with overriding the inherited Object.Equalsand Object.GetHashCodemethods) on all your custom types. In the case of composite types, invoke the contained types' Equalsmethod within the containing types. For contained collections, use the SequenceEqualextension method, which internally calls IEquatable<T>.Equalsor Object.Equalson each element. This approach will obviously require you to extend your types' definitions, but its results are faster than any generic solutions involving serialization.
实现IEquatable<T>(通常连同重写继承Object.Equals和Object.GetHashCode方法)对所有的自定义类型。对于复合类型,在包含类型中调用包含类型的Equals方法。对于包含的集合,请使用SequenceEqual扩展方法,该方法在每个元素上内部调用IEquatable<T>.Equals或Object.Equals。这种方法显然需要您扩展类型的定义,但其结果比任何涉及序列化的通用解决方案都要快。
Edit: Here is a contrived example with three levels of nesting.
编辑:这是一个具有三级嵌套的人为示例。
For value types, you can typically just call their Equalsmethod. Even if the fields or properties were never explicitly assigned, they would still have a default value.
对于值类型,您通常可以只调用它们的Equals方法。即使从未明确分配字段或属性,它们仍将具有默认值。
For reference types, you should first call ReferenceEquals, which checks for reference equality – this would serve as an efficiency boost when you happen to be referencing the same object. It would also handle cases where both references are null. If that check fails, confirm that your instance's field or property is not null (to avoid NullReferenceException) and call its Equalsmethod. Since our members are properly typed, the IEquatable<T>.Equalsmethod gets called directly, bypassing the overridden Object.Equalsmethod (whose execution would be marginally slower due to the type cast).
对于引用类型,您应该首先调用ReferenceEquals,它会检查引用相等性——当您碰巧引用同一个对象时,这将提高效率。它还可以处理两个引用都为空的情况。如果该检查失败,请确认您的实例的字段或属性不为空(以避免NullReferenceException)并调用其Equals方法。由于我们的成员类型正确,因此IEquatable<T>.Equals直接调用该方法,绕过覆盖的Object.Equals方法(由于类型转换,其执行速度会稍微慢一些)。
When you override Object.Equals, you're also expected to override Object.GetHashCode; I didn't do so below for the sake of conciseness.
当您覆盖时Object.Equals,您还需要覆盖Object.GetHashCode; 为了简洁起见,我没有在下面这样做。
public class Person : IEquatable<Person>
{
public int Age { get; set; }
public string FirstName { get; set; }
public Address Address { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Person);
}
public bool Equals(Person other)
{
if (other == null)
return false;
return this.Age.Equals(other.Age) &&
(
object.ReferenceEquals(this.FirstName, other.FirstName) ||
this.FirstName != null &&
this.FirstName.Equals(other.FirstName)
) &&
(
object.ReferenceEquals(this.Address, other.Address) ||
this.Address != null &&
this.Address.Equals(other.Address)
);
}
}
public class Address : IEquatable<Address>
{
public int HouseNo { get; set; }
public string Street { get; set; }
public City City { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as Address);
}
public bool Equals(Address other)
{
if (other == null)
return false;
return this.HouseNo.Equals(other.HouseNo) &&
(
object.ReferenceEquals(this.Street, other.Street) ||
this.Street != null &&
this.Street.Equals(other.Street)
) &&
(
object.ReferenceEquals(this.City, other.City) ||
this.City != null &&
this.City.Equals(other.City)
);
}
}
public class City : IEquatable<City>
{
public string Name { get; set; }
public override bool Equals(object obj)
{
return this.Equals(obj as City);
}
public bool Equals(City other)
{
if (other == null)
return false;
return
object.ReferenceEquals(this.Name, other.Name) ||
this.Name != null &&
this.Name.Equals(other.Name);
}
}
Update: This answer was written several years ago. Since then, I've started to lean away from implementing IEquality<T>for mutable types for such scenarios. There are two notions of equality: identityand equivalence. At a memory representation level, these are popularly distinguished as “reference equality” and “value equality” (see Equality Comparisons). However, the same distinction can also apply at a domain level. Suppose that your Personclass has a PersonIdproperty, unique per distinct real-world person. Should two objects with the same PersonIdbut different Agevalues be considered equal or different? The answer above assumes that one is after equivalence. However, there are many usages of the IEquality<T>interface, such as collections, that assume that such implementations provide for identity. For example, if you're populating a HashSet<T>, you would typically expect a TryGetValue(T,T)call to return existing elements that share merely the identity of your argument, not necessarily equivalent elements whose contents are completely the same. This notion is enforced by the notes on GetHashCode:
更新:这个答案是几年前写的。从那时起,我开始不再IEquality<T>为此类场景实现可变类型。平等有两个概念:同一性和等价性。在内存表示级别,它们通常被区分为“引用相等”和“值相等”(参见相等比较)。但是,同样的区别也适用于域级别。假设你的Person班级有一个PersonId属性,每个不同的现实世界的人都是独一无二的。具有相同PersonId但不同Age值的两个对象应该被视为相等还是不同?上面的答案假设一个是在等价之后。但是,有很多用法IEquality<T>接口,例如集合,假设此类实现提供了identity。例如,如果您正在填充HashSet<T>,您通常希望TryGetValue(T,T)调用返回仅共享参数标识的现有元素,不一定是内容完全相同的等效元素。这个概念由关于 的注释强制执行GetHashCode:
In general, for mutable reference types, you should override
GetHashCode()only if:
- You can compute the hash code from fields that are not mutable; or
- You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.
通常,对于可变引用类型,您应该
GetHashCode()仅在以下情况下覆盖:
- 您可以从不可变的字段计算哈希码;或者
- 您可以确保可变对象的哈希码在对象包含在依赖其哈希码的集合中时不会更改。
回答by PaulG
I would say that:
我会这样说:
Object1.Equals(Object2)
Object1.Equals(Object2)
would be what you're looking for. That's if you're looking to see if the objects are the same, which is what you seem to be asking.
将是你要找的。那是如果您想查看对象是否相同,这似乎是您要问的。
If you want to check to see if all the child objects are the same, run them through a loop with the Equals()method.
如果您想检查所有子对象是否相同,请使用该Equals()方法通过循环运行它们。
回答by payo
I'll assume you are not referring to literally the same objects
我假设你不是指字面上相同的对象
Object1 == Object2
You might be thinking about doing a memory comparison between the two
您可能正在考虑在两者之间进行内存比较
memcmp(Object1, Object2, sizeof(Object.GetType())
But that's not even real code in c# :). Because all of your data is probably created on the heap, the memory is not contiguous and you can't just compare the equality of two objects in an agnostic manner. You're going to have to compare each value, one at a time, in a custom way.
但这在 c# 中甚至都不是真正的代码 :)。因为您的所有数据可能都是在堆上创建的,所以内存不是连续的,您不能仅以不可知的方式比较两个对象的相等性。您将不得不以自定义方式一次比较每个值。
Consider adding the IEquatable<T>interface to your class, and define a custom Equalsmethod for your type. Then, in that method, manual test each value. Add IEquatable<T>again on enclosed types if you can and repeat the process.
考虑将IEquatable<T>接口添加到您的类中,并Equals为您的类型定义自定义方法。然后,在该方法中,手动测试每个值。IEquatable<T>如果可以,再次添加封闭类型并重复该过程。
class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
/* check all the values */
return false;
}
}
回答by goric
One way to do this would be to override Equals()on each type involved. For example, your top level object would override Equals()to call the Equals()method of all 5 child objects. Those objects should all override Equals()as well, assuming they are custom objects, and so on until the entire hierarchy could be compared by just performing an equality check on the top level objects.
一种方法是覆盖所Equals()涉及的每种类型。例如,您的顶级对象将覆盖Equals()以调用Equals()所有 5 个子对象的方法。这些对象也应该全部覆盖Equals(),假设它们是自定义对象,依此类推,直到可以通过对顶级对象执行相等检查来比较整个层次结构。
回答by Master Chief
Use IEquatable<T>Interface which has a method Equals.
使用IEquatable<T>具有方法的接口Equals。
回答by JotaBe
If you don't want to implement IEquatable, you can always use Reflection to compare all the properties: - if they're value type, just compare them -if they are reference type, call the function recursively to compare its "inner" properties.
如果您不想实现 IEquatable,您始终可以使用反射来比较所有属性: - 如果它们是值类型,只需比较它们 - 如果它们是引用类型,则递归调用该函数以比较其“内部”属性.
I'm not thinking about performace, but about simplicity. It depends, however on the exact design of your objects. It could get complicated depending on your objects shape (for example if there are cyclic dependencies between properties). There are, however, several solutions out there that you can use, like this one:
我不是在考虑性能,而是在考虑简单性。但是,这取决于对象的确切设计。根据您的对象形状(例如,如果属性之间存在循环依赖关系),它可能会变得复杂。但是,您可以使用多种解决方案,例如:
Another option is to serialize the object as text, for example using JSON.NET, and comparing the serialization result. (JSON.NET can handle Cyclic dependencies between properties).
另一种选择是将对象序列化为文本,例如使用 JSON.NET,并比较序列化结果。(JSON.NET 可以处理属性之间的循环依赖)。
I don't know if by fastest you mean the fastest way to implement it or a code that runs fast. You should not optimize before knowing if you need to. Premature optimization is the root of all evil
我不知道最快是指实现它的最快方法还是运行速度快的代码。在知道是否需要之前,您不应该进行优化。过早优化是万恶之源
回答by JoelFan
Serialize both objects and compare the resulting strings
序列化两个对象并比较结果字符串
回答by ozzy432836
Serialize both objects and compare the resulting strings by @JoelFan
序列化两个对象并通过@JoelFan 比较结果字符串
So to do this, create a static class like so and use Extensions to extend ALL objects (so you can pass anytype of object, collection, etc into the method)
所以要做到这一点,创建一个像这样的静态类并使用扩展来扩展所有对象(这样你就可以将任何类型的对象、集合等传递到方法中)
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.Text;
public static class MySerializer
{
public static string Serialize(this object obj)
{
var serializer = new DataContractJsonSerializer(obj.GetType());
using (var ms = new MemoryStream())
{
serializer.WriteObject(ms, obj);
return Encoding.Default.GetString(ms.ToArray());
}
}
}
Once you reference this static class in any other file, you can do this:
在任何其他文件中引用此静态类后,您可以执行以下操作:
Person p = new Person { Firstname = "Jason", LastName = "Argonauts" };
Person p2 = new Person { Firstname = "Jason", LastName = "Argonaut" };
//assuming you have already created a class person!
string personString = p.Serialize();
string person2String = p2.Serialize();
Now you can simply use .Equals to compare them. I use this for checking if objects are in collections too. It works really well.
现在您可以简单地使用 .Equals 来比较它们。我用它来检查对象是否也在集合中。它真的很好用。
回答by Jonathan
You can use extension method, recursion to resolve this problem:
您可以使用扩展方法,递归来解决此问题:
public static bool DeepCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
//Compare two object's class, return false if they are difference
if (obj.GetType() != another.GetType()) return false;
var result = true;
//Get all properties of obj
//And compare each other
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
if (!objValue.Equals(anotherValue)) result = false;
}
return result;
}
public static bool CompareEx(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
//properties: int, double, DateTime, etc, not class
if (!obj.GetType().IsClass) return obj.Equals(another);
var result = true;
foreach (var property in obj.GetType().GetProperties())
{
var objValue = property.GetValue(obj);
var anotherValue = property.GetValue(another);
//Recursion
if (!objValue.DeepCompare(anotherValue)) result = false;
}
return result;
}
or compare by using Json (if object is very complex) You can use Newtonsoft.Json:
或使用 Json 进行比较(如果对象非常复杂)您可以使用 Newtonsoft.Json:
public static bool JsonCompare(this object obj, object another)
{
if (ReferenceEquals(obj, another)) return true;
if ((obj == null) || (another == null)) return false;
if (obj.GetType() != another.GetType()) return false;
var objJson = JsonConvert.SerializeObject(obj);
var anotherJson = JsonConvert.SerializeObject(another);
return objJson == anotherJson;
}
回答by Akshay
I found this below function for comparing objects.
我在下面找到了用于比较对象的函数。
static bool Compare<T>(T Object1, T object2)
{
//Get the type of the object
Type type = typeof(T);
//return false if any of the object is false
if (object.Equals(Object1, default(T)) || object.Equals(object2, default(T)))
return false;
//Loop through each properties inside class and get values for the property from both the objects and compare
foreach (System.Reflection.PropertyInfo property in type.GetProperties())
{
if (property.Name != "ExtensionData")
{
string Object1Value = string.Empty;
string Object2Value = string.Empty;
if (type.GetProperty(property.Name).GetValue(Object1, null) != null)
Object1Value = type.GetProperty(property.Name).GetValue(Object1, null).ToString();
if (type.GetProperty(property.Name).GetValue(object2, null) != null)
Object2Value = type.GetProperty(property.Name).GetValue(object2, null).ToString();
if (Object1Value.Trim() != Object2Value.Trim())
{
return false;
}
}
}
return true;
}
I am using it and it is working fine for me.
我正在使用它,它对我来说很好用。

