为什么要实现IEquatable <T>接口

dev*_*zim 3 c# asp.net polymorphism interface

我一直在阅读文章并理解一定程度的接口,如果我想改正我自己的自定义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));
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

现在,如果我不实现接口并: IEquatable<Address>省略代码,似乎应用程序运行完全相同.因此,我不清楚为什么要实现这个界面?我可以在没有它的情况下编写我自己的自定义Equals方法,断点将仍然触及方法并返回相同的结果.任何人都可以帮我解释一下这个吗?IEquatable<Address>在调用Equals方法之前,我挂断了为什么包含" ".

Jon*_*eet 11

现在,如果我不实现接口并离开:IEquatable代码,似乎应用程序运行完全相同.

那么,这取决于"应用程序"的作用.例如:

List<Address> addresses = new List<Address>
{
    new Address { ... }
};
int index = addresses.IndexOf(new Address { ... });
Run Code Online (Sandbox Code Playgroud)

... index如果你没有被覆盖Equals(object)也没有实现,那将无法工作(即将为-1)IEquatable<T>.List<T>.IndexOf不会叫你Equals超负荷.

知道你的特定类的代码将获取Equals重载 - 但是任何与任意对象一起工作的代码(例如泛型集合,所有LINQ to Objects等)都不会获取它.


Gyö*_*zeg 5

.NET框架在进行相等性检查时有许多令人困惑的可能性:

  1. 虚拟的 Object.Equals(object)
  2. 可重载相等运算符(==!=<=>=
  3. IEquatable<T>.Equals(T)
  4. IComparable.CompareTo(object)
  5. IComparable<T>.CompareTo(T)
  6. IEqualityComparer.Equals(object, object)
  7. IEqualityComparer<T>.Equals(T, T)
  8. IComparer.Compare(object, object)
  9. 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>存在接口的原因。如果要在泛型集合中使用它们,则应在结构上实现它。我已经提到保持一致性吗?
  • 默认情况下,==and !=运算符不能用于结构,因此如果要使用它们,则必须重载它们。只需调用强类型的IEquatable<T>.Equals(T)实现。
  • 与类相似,如果对您的类型有意义,那么大或小意味着有意义,请实现该IComparable接口。在结构的情况下,你应该实现IComparable<T>以及把事情高性能(例如Array.SortList<T>.BinarySearch使用类型的关键SortedList<TKey, TValue>,等等)。如果你超载了==!=运营商,你应该做的<><=>=,太。

一点附录: 如果您必须使用具有不正确比较逻辑的类型来满足您的需要,则可以使用列表中从6到9的接口。在这里,您可以忘记一致性(至少要考虑Equals类型的自身),并且可以实现可用于基于散列和排序的集合的自定义比较。