我有一个类,它包含一些字符串成员,一些双成员和一些数组对象。
我创建了这个类的两个对象,有没有最简单,有效的方法来比较这些对象并说它们相等?有什么建议?
我知道如何编写比较函数,但这会耗费时间。
答案 0 :(得分:11)
实际执行此操作的唯一方法是覆盖bool Object.Equals(object other)
以在满足相等条件时返回true,否则返回false。您还必须覆盖int Object.GetHashCode()
以返回在覆盖Equals()
时从您考虑的数据所有计算的int。
另外,请注意GetHashCode()
的合同指定当Equals()
在比较它们时返回true时,两个对象的返回值必须相等。这意味着return 0;
是GetHashCode()
的有效实现,但是当您的类的对象用作字典键或存储在HashSet<T>
中时,它会导致效率低下。
我实现平等的方式是这样的:
public class Foo : IEquatable<Foo>
{
public bool Equals(Foo other)
{
if (other == null)
return false;
if (other == this)
return true; // Same object reference.
// Compare this to other and return true/false as appropriate.
}
public override bool Equals(Object other)
{
return Equals(other as Foo);
}
public override int GetHashCode()
{
// Compute and return hash code.
}
}
实现GetHashCode()
的一种简单方法是将Equals()
中您认为相等的所有数据的哈希码异或。因此,例如,如果您比较的属性为string FirstName; string LastName; int Id;
,则您的实现可能如下所示:
public override int GetHashCode()
{
return (FirstName != null ? FirstName.GetHashCode() : 0) ^
(LastName != null ? LastName.GetHashCode() : 0) ^
Id; // Primitives of <= 4 bytes are their own hash codes
}
我通常不覆盖相等运算符,因为大多数时候我只关心字典键或集合的相等性。我只考虑重写等于运算符,如果你可能按值而不是通过引用进行更多的比较,因为它在语法上不那么详细。但是,您必须记住更改在对象上使用==
或!=
的所有位置(包括在Equals()
的实现中!)以使用Object.ReferenceEquals()
,或者将两个操作数转换为object
。这个讨厌的问题(如果你不小心,可以在你的相等测试中导致无限递归)是我很少覆盖这些操作符的主要原因之一。
答案 1 :(得分:9)
在.NET中执行此操作的“正确”方法是为您的类实现IEquatable接口:
public class SomeClass : IEquatable<SomeClass>
{
public string Name { get; set; }
public double Value { get; set; }
public int[] NumberList { get; set; }
public bool Equals(SomeClass other)
{
// whatever your custom equality logic is
return other.Name == Name &&
other.Value == Value &&
other.NumberList == NumberList;
}
}
但是,如果你真的想要做得对,this isn't all you should do。您还应该重写Equals(对象,对象)和GetHashCode(对象)方法,以便无论您的调用代码如何比较相等(可能在字典中或可能在某些松散类型的集合中),您的代码而不是引用 - 类型相等将是决定因素:
public class SomeClass : IEquatable<SomeClass>
{
public string Name { get; set; }
public double Value { get; set; }
public int[] NumberList { get; set; }
/// <summary>
/// Explicitly implemented IEquatable method.
/// </summary>
public bool IEquatable<SomeClass>.Equals(SomeClass other)
{
return other.Name == Name &&
other.Value == Value &&
other.NumberList == NumberList;
}
public override bool Equals(object obj)
{
var other = obj as SomeClass;
if (other == null)
return false;
return ((IEquatable<SomeClass>)(this)).Equals(other);
}
public override int GetHashCode()
{
// Determine some consistent way of generating a hash code, such as...
return Name.GetHashCode() ^ Value.GetHashCode() ^ NumberList.GetHashCode();
}
}
答案 2 :(得分:5)
花了一整天的时间编写一个扩展方法,通过反映一个具有各种复杂逻辑的对象的属性来处理不同的属性类型并实际上使它接近良好,然后在16:55我突然意识到如果你序列化这两个对象,你只需要比较两个字符串...... duh
所以这是一个简单的序列化程序扩展方法,甚至适用于字典
public static class TExtensions
{
public static string Serialize<T>(this T thisT)
{
var serializer = new DataContractSerializer(thisT.GetType());
using (var writer = new StringWriter())
using (var stm = new XmlTextWriter(writer))
{
serializer.WriteObject(stm, thisT);
return writer.ToString();
}
}
}
现在您的测试可以像
一样简单Asset.AreEqual(objA.Serialise(), objB.Serialise())
尚未进行过广泛的测试,但看起来很有前途,更重要的是,简单。无论哪种方式仍然是一个有用的方法,你的实用程序设置正确吗?
答案 3 :(得分:1)
最好的答案是为您的类实现IEquatable - 它可能不是您想要听到的答案,但这是在.NET中实现值等效的最佳方法。
另一种选择是计算你班级所有成员的唯一哈希值,然后对这些成员进行价值比较,但这比编写比较函数更有用;)
答案 4 :(得分:1)
由于这些是对象,我的猜测是你必须覆盖对象的Equals方法。否则,只有当两个对象都引用同一个对象时,Equals方法才会给你ok。
我知道这不是你想要的答案。但是由于您班级中的属性很少,您可以轻松覆盖该方法。