我一直在阅读文章并了解某种程度的接口,但是,如果我想改正我自己的自定义Equals方法,似乎我可以在不实现IEquatable接口的情况下完成此操作。一个例子。
using System;
using System.Collections;
using System.ComponentModel;
namespace ProviderJSONConverter.Data.Components
{
public class Address : IEquatable<Address>
{
public string address { get; set; }
[DefaultValue("")]
public string address_2 { get; set; }
public string city { get; set; }
public string state { get; set; }
public string zip { get; set; }
public bool Equals(Address other)
{
if (Object.ReferenceEquals(other, null)) return false;
if (Object.ReferenceEquals(this, other)) return true;
return (this.address.Equals(other.address)
&& this.address_2.Equals(other.address_2)
&& this.city.Equals(other.city)
&& this.state.Equals(other.state)
&& this.zip.Equals(other.zip));
}
}
}
现在,如果我不实现接口并将: IEquatable<Address>
从代码中删除,那么应用程序似乎运行完全相同。因此,我不清楚为什么要实现这个界面?我可以在没有它的情况下编写我自己的自定义Equals方法,断点将仍然触及方法并返回相同的结果。
任何人都可以帮我解释一下这个吗?我为什么要加入&#34; IEquatable<Address>
&#34;在调用Equals方法之前。
答案 0 :(得分:10)
现在如果我没有实现接口并且从代码中删除IEquatable,那么应用程序似乎运行完全相同。
那么,这取决于什么&#34;应用程序&#34;确实。例如:
List<Address> addresses = new List<Address>
{
new Address { ... }
};
int index = addresses.IndexOf(new Address { ... });
...如果您既没有覆盖index
也没有实施Equals(object)
,那么它将无法工作(即IEquatable<T>
将为-1)。 List<T>.IndexOf
无法调用Equals
超载。
了解特定类的代码将获取Equals
重载 - 但任何代码(例如泛型集合,所有LINQ to Objects等)都可以与一起使用任意对象都不会被捡起来。
答案 1 :(得分:4)
.NET框架对于等式检查有很多可能的混淆:
Object.Equals(object)
==
,!=
,<=
,>=
)IEquatable<T>.Equals(T)
IComparable.CompareTo(object)
IComparable<T>.CompareTo(T)
IEqualityComparer.Equals(object, object)
IEqualityComparer<T>.Equals(T, T)
IComparer.Compare(object, object)
IComparer<T>.Compare(T, T)
我没有提到ReferenceEquals
,静态Object.Equals(object, object)
和特殊情况(例如字符串和浮点数比较),只是我们可以实现某些内容的情况。
此外,前两个点的默认行为对于结构和类是不同的。因此,用户可能会对实施内容和方式感到困惑并不奇怪。
作为规则的拇指,您可以遵循以下模式:
Equals(object)
方法和相等运算符(==
,!=
)都会检查 引用相等 。Equals
方法(以及GetHashCode
;否则,您的类将无法在哈希集合中使用)==
和!=
运算符的原始引用相等功能,这对于类来说很常见。但如果你重载它们,它必须与Equals
一致。IComparable
接口。当Equals
报告相等时,CompareTo
必须返回0(再次,一致性)。基本上就是这样。为类实现通用IEquatable<T>
和Comparable<T>
接口不是必须的:因为没有装箱,所以通用集合中的性能增益最小。但请记住,如果您实施它们, 保持一致性 。
Equals(object)
为结构执行 值比较 (检查字段值)。虽然通常这是值类型的预期行为,但基本实现通过使用具有可怕性能的反射来实现。因此,即使您实现了与原始结构相同的功能,也始终覆盖公共结构中的Equals(object)
。Equals(object)
方法用于结构时,会发生装箱,这会产生性能成本(不如ValueType.Equals
中的反射差,但重要)。这就是IEquatable<T>
接口存在的原因。如果要在泛型集合中使用它们,则应在结构上实现它。我是否已经提到要保持一致性?==
和!=
运算符不能用于结构,因此如果要使用它们,必须重载它们。只需调用强类型IEquatable<T>.Equals(T)
实现。IComparable
接口。对于结构体,您应该实现IComparable<T>
以使效果更好(例如Array.Sort
,List<T>.BinarySearch
,使用类型作为SortedList<TKey, TValue>
中的键,等等)。如果您重载了==
,!=
运算符,则应该对<
,>
,<=
,>=
执行此操作。 一个小附录:
如果必须使用具有不合适比较逻辑的类型,则可以使用列表中从6.到9.的接口。这是您可以忘记一致性的地方(至少考虑类型的自Equals
),您可以实现可以在基于散列的集合和已排序集合中使用的自定义比较。
答案 2 :(得分:2)
如果你覆盖了Equals(object obj)
方法,那么这只是表演问题,如下所示:What's the difference between IEquatable and just overriding Object.Equals()?
但是,只要您没有覆盖Equals(object obj)
但提供了自己的强类型Equals(Adddress obj)
方法,没有实施IEquatable<T>
,您就不会向所有类别指出依靠此接口的实现来进行比较,你有自己应该使用的Equals方法。
因此,正如John Skeet所指出的,EqualityComparer<Address>.Default
用于比较地址的List<Address>.IndexOf
属性无法知道它应该使用您的Equals
方法。
答案 3 :(得分:1)
IEquatable接口只是添加了Equals方法,无论我们在泛型参数中提供什么类型。然后功能超载负责休息。
如果我们将IEquatable添加到Employee结构中,那么可以将该对象与Employee对象进行比较,而无需任何类型转换。虽然我们可以使用默认的Equals方法来实现,它接受Object作为param, 因此从Object转换为struct涉及Boxing。因此,拥有IEquatable&lt; Employee&gt;将提高绩效。
例如,假设我们想要将员工结构与另一名员工进行比较if(e1.Equals(e2))
{
//do some
}
对于上面的示例,它将使用Equals with Employee作为参数。所以不需要拳击或拆箱
struct Employee : IEquatable<Employee>
{
public int Id { get; set; }
public bool Equals(Employee other)
{
//no boxing not unboxing, direct compare
return this.Id == other.Id;
}
public override bool Equals(object obj)
{
if(obj is Employee)
{ //un boxing
return ((Employee)obj).Id==this.Id;
}
return base.Equals(obj);
}
}
更多示例:
Int结构实现IEquatable&lt; int&gt;
Bool结构实现IEquatable&lt; bool&gt;
Float结构实现IEquatable&lt; float&gt;
因此,如果你调用someInt.Equals(1),它就不会触发Equals(object)方法。它会触发Equals(int)方法。