为什么要实施公平教育?接口
本文关键字:接口 施公平 为什么 | 更新日期: 2023-09-27 18:11:06
我一直在阅读文章并在一定程度上理解接口,但是,如果我想纠正我自己的自定义Equals方法,似乎我可以在不实现等价接口的情况下做到这一点。一个例子。
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方法,而不需要它,断点仍然会击中方法并返回相同的结果。有人能帮我多解释一下吗?我挂断了为什么包括"IEquatable<Address>
"之前调用Equals方法。
现在,如果我不实现接口,并在代码中留下:IEquatable,看起来应用程序的操作完全相同。
嗯,这取决于"应用程序"做什么。例如:
List<Address> addresses = new List<Address>
{
new Address { ... }
};
int index = addresses.IndexOf(new Address { ... });
…这将不起作用(即index
将是-1),如果你既没有覆盖Equals(object)
也没有实现IEquatable<T>
。List<T>.IndexOf
不会调用你的Equals
重载。
代码知道你的特定的类将拾取Equals
过载-但任何代码(例如泛型集合,所有的LINQ到对象等),只与任意对象工作将不会拾取它。
.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。在列表中。这是您可以忘记一致性的地方(至少考虑到类型的self Equals
),您可以实现可用于基于哈希和排序的集合的自定义比较。
如果您已经覆盖了Equals(object obj)
方法,那么这将只是性能问题,正如这里所指出的:IEquatable和仅仅覆盖Object.Equals()之间的区别是什么?
但是只要你没有重写Equals(object obj)
,而是提供了你自己的强类型Equals(Adddress obj)
方法,没有实现IEquatable<T>
,你没有告诉所有依赖于这个接口实现来操作比较的类,你有自己的Equals方法应该使用。
因此,正如John Skeet所指出的,List<Address>.IndexOf
用于比较地址的EqualityComparer<Address>.Default
属性将无法知道它应该使用Equals
方法。
IEquatable接口只是用我们在泛型参数中提供的任何类型添加Equals方法。然后函数重载处理剩余部分。
如果在Employee结构中添加等价对象,则该对象可以与Employee对象进行比较,而无需进行类型转换。虽然我们也可以用默认的Equals方法来实现,它接受Object作为参数,所以从对象到结构的转换涉及到装箱。因此,有了平等的"雇员"。将提高性能。
例如,假设我们想比较Employee结构与另一个Employee
if(e1.Equals(e2))
{
//do some
}
对于上面的例子,它将使用Employee作为参数的Equals。因此不需要装箱或拆箱
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结构实现了可等价的
Bool结构实现了等价的
Float结构实现了
如果你调用someint。Equals(1)它不会触发Equals(object)方法。它触发Equals(int)方法