IEquatable和仅重写Object.Equals()有什么区别?


183

我希望我的Food课程能够在等于的另一个实例时进行测试Food。稍后将针对列表使用它,并且我想使用其List.Contains()方法。我应该实施IEquatable<Food>还是仅重写Object.Equals()?从MSDN:

此方法通过使用默认的相等比较器确定相等,该比较器由对象的IEquatable.Equals方法实现,用于T(列表中值的类型)。

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



Answers:


213

主要原因是性能。当泛型在.NET 2.0中引入他们能够添加一堆整齐班如List<T>Dictionary<K,V>HashSet<T>,等这些结构大量使用GetHashCodeEquals。但是对于值类型,这需要装箱。IEquatable<T>使结构实现强类型Equals方法,因此不需要装箱。因此,在将值类型与泛型集合一起使用时,性能会更好。

引用类型并没有带来太多好处,但是该IEquatable<T>实现确实使您避免了强制转换,System.Object如果频繁调用强制转换会有所不同。

正如Jared Parson的博客所述,您仍然必须实现Object覆盖。


引用类型之间是否有任何转换?我一直认为,强制转换只是将某种不明显的强制转换从一种对象分配给另一种对象时对编译器所做的“声明”。这是在编译之后,代码甚至都不知道那里有强制转换。
吞噬了极乐世界

7
在C ++中确实如此,但在强制类型安全的.NET语言中却不是。有一个运行时强制类型转换,如果强制类型转换不成功,则会引发异常。因此,需要为转换支付少量运行时间损失。编译器可以优化远离的上行流,例如,对象o =(object)“ string”; 但是垂头丧气-字符串s =(string)o; -必须在运行时发生。
乔什(Josh)

1
我懂了。您是否有机会在任何地方获得有关.NET的那种“更深入”的信息?谢谢!
吞噬了极乐世界

7
我建议通过Jeff Richter的C#和Jon Skeet的Depth的C#建议CLR。至于博客,博客Wintellect的都不错,MSDN博客等
乔希

IEquatable<T>除了提醒开发人员public bool Equals(T other) 在类或结构中包含成员外,界面是否还要做其他事情 ?接口的存在与否在运行时没有区别。的超载Equals似乎是所需要的。
mikemay

47

根据MSDN

如果实现了IEquatable<T>,你也应该重写的基类的实现 Object.Equals(Object),并GetHashCode 让他们的行为与该一致的IEquatable<T>.Equals 方法。如果您重写 Object.Equals(Object),则在Equals(System.Object, System.Object)对类的静态方法的调用中也会调用被重写的实现。这样可以确保该Equals方法的所有调用都返回一致的结果。

因此,似乎两者之间没有真正的功能差异,只是可以根据类的使用方式调用两者中的任何一个。从性能的角度来看,最好使用通用版本,因为没有与此相关的装箱/拆箱损失。

从逻辑的角度来看,最好实现该接口。覆盖该对象并不能真正告诉任何人您的类实际上是平等的。覆盖可能只是不执行任何操作的类或浅层的实现。使用该接口明确表示:“嘿,这对于相等性检查是有效的!” 这只是更好的设计。


9
如果结构要用作Dictionary或类似集合中的键,则结构肯定应实现iEquatable(其OwnType);它将大大提高性能。通过实现IEquatable(ofOwnType),非继承类将获得稍微的性能提升。可继承类应该//不//实现IEquatable。
supercat 2010年

30

用一个实际的例子来扩展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() { }
}

这样,我就有了可重用的Equals()方法,该方法对于我所有的派生类都可以使用。


再问一个问题。使用“ obj作为EntityBase”而不是(EntityBase)obj有什么好处?只是样式问题还是根本没有优势?
吞噬了极乐世界

22
在“ obj作为EntityBase”的情况下-如果obj不是EntityBase类型,它将传递“ null”并继续而没有任何错误或异常,但是在“(EntityBase)obj”的情况下,它将强制尝试投射obj到EntityBase,如果obj的类型不是EntityBase,它将抛出InvalidCastException。是的,“ as”只能应用于引用类型。
这个。__curious_geek

1
Josh到Jared Par的博客的链接似乎表明您还需要重写GetHashCode。不是吗?
2014年

3
我并没有真正获得您的实现所提供的额外价值。您能否澄清抽象基类要解决的问题?
Mert Akcakaya 2015年

1
@Amicable-是的,每当重写Object.Equals(Object)时,还必须重写GetHashCode,以便容器起作用。
纳姆福德2015年

0

如果调用object.Equals,则将迫使对值类型进行昂贵的装箱。在对性能敏感的情况下,这是不希望的。解决方法是使用IEquatable<T>

public interface IEquatable<T>
{
  bool Equals (T other);
}

背后的想法IEquatable<T>是,它产生的结果与以前相同,object.Equals但速度更快。该约束where T : IEquatable<T>必须与以下通用类型一起使用。

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

否则,它绑定到slower object.Equals()

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.