C# 什么';IEquatable和仅覆盖Object.Equals()之间的区别是什么?

C# 什么';IEquatable和仅覆盖Object.Equals()之间的区别是什么?,c#,.net,equals,equality,iequatable,C#,.net,Equals,Equality,Iequatable,我希望我的Food类能够在与Food的另一个实例相等时进行测试。稍后,我将对列表使用它,我想使用它的List.Contains()方法。我应该实现IEquatable还是只重写Object.Equals()?从MSDN: 此方法通过以下方式确定相等性: 使用默认的相等比较器, 由对象的 实施 T的等式方法 (列表中值的类型) 所以我的下一个问题是:.NETFramework的哪些函数/类使用了Object.Equals()?我应该首先使用它吗?主要原因是性能。当泛型在.NET 2.0中引入时,它

我希望我的
Food
类能够在与
Food
的另一个实例相等时进行测试。稍后,我将对列表使用它,我想使用它的
List.Contains()
方法。我应该实现
IEquatable
还是只重写
Object.Equals()
?从MSDN:

此方法通过以下方式确定相等性: 使用默认的相等比较器, 由对象的 实施 T的等式方法 (列表中值的类型)


所以我的下一个问题是:.NETFramework的哪些函数/类使用了
Object.Equals()
?我应该首先使用它吗?

主要原因是性能。当泛型在.NET 2.0中引入时,它们能够添加一系列整洁的类,如
List
Dictionary
HashSet
,等等。这些结构大量使用
GetHashCode
Equals
。但对于值类型,这需要装箱
IEquatable
允许结构实现强类型的
Equals
方法,因此不需要装箱。因此,在将值类型与泛型集合一起使用时,性能会更好

引用类型没有那么大的好处,但是
IEquatable
实现确实让您避免了
System.Object
的强制转换,如果频繁调用它,则会产生不同的效果

但是,如上所述,您必须仍然实现标准的
对象。Equals
对象。GetHashcode
覆盖。

根据:

如果实现了
IEquatable
,则 也应该重写基类 实现
Object.Equals(Object)
GetHashCode
所以他们的行为是一致的 与
i相等。等于

方法。如果你真的超控
Object.Equals(Object)
,您的 在调用中也调用实现 对于静态
等于(System.Object,
对象)
方法。 这确保了
等于
方法返回一致的 结果

因此,这两个类之间似乎没有真正的功能区别,只是可以根据类的使用方式调用其中一个。从性能角度来看,最好使用通用版本,因为它没有装箱/拆箱惩罚


从逻辑的角度来看,实现接口也更好。重写对象并不会真正告诉任何人您的类实际上是可平等的。重写可能只是一个不做任何事情的类或一个浅层实现。使用接口明确地说,“嘿,这个东西对于等式检查是有效的!”这只是更好的设计。

用一个实际的例子扩展了Josh所说的内容+1给乔希-我正要在我的回答中写同样的话

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}
公共抽象类EntityBase:IEquatable
{
公共EntityBase(){}
#区域可容纳成员
公共布尔等于(EntityBase其他)
{
//使用派生类实例上的反射实现相等。
返回true;
}
公共覆盖布尔等于(对象对象对象)
{
返回此.Equals(obj作为EntityBase);
}
#端区
}
公共类作者:EntityBase
{
公共作者(){}
}
公共类书:EntityBase
{
公共图书(){}
}

这样,我就有了一个可重用的Equals()方法,它对我所有的派生类都是开箱即用的。

如果我们调用
object.Equals
,它会强制对值类型进行昂贵的装箱。这在性能敏感的场景中是不可取的。解决方案是使用
IEquatable


否则,它将绑定到
较慢的object.Equals()

引用类型之间是否有强制转换?我一直认为,当您从一种对象向另一种对象分配一些不明显的强制转换时,强制转换只是您对编译器所做的“语句”。这是在编译后,代码甚至不知道那里有一个强制转换,这在C++中是正确的,但不是.NET语言,它强制类型安全。存在运行时强制转换,如果强制转换未成功,将引发异常。因此,有一个小的运行时罚款支付铸造。编译器可以优化上传,例如对象o=(对象)“string”;但是向下转换-string s=(string)o;-必须在运行时发生。我明白了。碰巧你有什么地方可以让我获得关于.NET的那种“更深入”的信息?谢谢我建议大家通过杰夫·里克特的《C#》和乔恩·斯基特的《C#纵深》推荐CLR。至于博客,Wintellect博客很好,msdn博客等等。
IEquatable
接口除了提醒开发人员在类或结构中包含一个
public bool Equals(T other)
成员之外,还有什么作用吗?接口的存在与否在运行时没有区别。
Equals
的重载似乎是所有必要的。只需再问一个问题。使用“obj作为EntityBase”而不是(EntityBase)obj有什么好处?只是风格问题还是有任何优势?在“obj作为EntityBase”的情况下-如果obj不是EntityBase类型,它将通过“null”并继续,没有任何错误或异常,但如果是“(EntityBase)obj”,它将强制尝试将obj强制转换为EntityBase,如果obj不是EntityBase类型,它将抛出InvalidCastException。是的,“as”只能应用于引用类型。Josh指向Jared Par博客的链接似乎表明您还需要覆盖GetHashCode。不是这样吗?我真的没有得到您的实现提供的额外价值。您能澄清抽象基类解决的问题吗?@Amicable-是的,每当您重写Object.Equals(Object)时,您还必须重写GetHashCode以便容器工作。结构肯定应该实现
public interface IEquatable<T>
{
  bool Equals (T other);
}
public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}