【问题标题】:== or .Equals()== 或 .Equals()
【发布时间】:2008-09-27 21:36:37
【问题描述】:

为什么要使用一个而不是另一个?

【问题讨论】:

标签: c#


【解决方案1】:

== 是身份测试。如果被测试的两个对象实际上是同一个对象,它将返回 true。 Equals() 执行相等测试,如果两个对象认为自己相等,则返回 true。

身份测试速度更快,因此您可以在不需要更昂贵的相等性测试时使用它。例如,与null 或空字符串进行比较。

可以重载其中任何一个以提供不同的行为——例如对Equals() 的身份测试——但为了任何人阅读您的代码,请不要这样做。


在下面指出:StringDateTime 等某些类型为 == 运算符提供重载,从而使其具有相等语义。因此,确切的行为将取决于您要比较的对象的类型。


另见:

【讨论】:

  • @Brett:不知道,但 MSDN 博客说有可能。
  • 实际上,如果您还覆盖运算符 ==,FxCop 会强制您覆盖 Equals,因为与 ReferenceEquals 相比,它们应该基本相同。
  • 你怎么能说你永远不应该覆盖 Equals?如果您的对象是 Equal 但在应用程序的不同部分创建的(例如从数据库中提取两次的同一行),您可能会说它们是相同的。主要是Equals应该对应对象的身份
  • == 的重载方式与任何其他运算符相同。至于重载 .Equals(),如果您需要基于值而不是引用来衡量值相等,则应该这样做。如果不应该通过引用(即值类型)进行比较,你应该重载 ==
  • 因为 String 会覆盖 == 运算符并在使用 == 时调用 String.Equals(a, b)。所以 s == r 和 s.Equals(r) 对 String 做同样的事情。 Check the source code
【解决方案2】:

@约翰·米利金:

在下面指出:某些值类型(如 DateTime)为 == 运算符提供重载 > 赋予它相等语义。因此,确切的行为将取决于>您正在比较的对象的类型。

详细说明:

DateTime 被实现为一个结构。所有结构都是 System.ValueType 的子级。

由于 System.ValueType 的子对象存在于堆栈中,因此没有指向堆的引用指针,因此无法进行引用检查,您必须仅按值比较对象。

System.ValueType 覆盖 .Equals() 和 == 以使用基于反射的相等性检查,它使用反射来比较每个字段的值。

因为反射有点慢,如果你实现自己的结构,重要的是重写 .Equals() 并添加你自己的值检查代码,因为这样会快得多。不要只调用 base.Equals();

【讨论】:

  • 日期时间可能存在于堆或堆栈中。例如,如果它在一个类中,它将存在于堆中;基本上,如果两个值类型是按值而不是按引用传递的,它们就永远不会相同,对吧?
【解决方案3】:

其他人几乎都已经涵盖了你,但我还有一点建议。时不时地,你会看到有人以他的生命(以及他所爱的人)发誓.Equals 是更有效/更好/最佳实践或其他一些教条主义的路线。我不能谈效率(好吧,好吧,在某些情况下我可以),但我可以谈一个会突然出现的大问题:.Equals 需要一个对象存在。 (听起来很愚蠢,但它会让人们失望。)

您不能执行以下操作:

StringBuilder sb = null;
if (sb.Equals(null))
{
    // whatever
}

对我,也许是大多数人来说,很明显你会得到一个NullReferenceException。然而,.Equals 的支持者忘记了那个小事实。当他们看到 NullRefs 开始弹出时,有些甚至被“抛出”(抱歉,无法抗拒)。

(在DailyWTF 发布之前的几年,我确实与一个强制所有平等检查都是.Equals 而不是== 的人一起工作。即使证明他的不准确也无济于事. 我们只是确保打破了他所有的其他规则,以使从方法或属性返回的引用永远不会为空,并且最终解决了。)

【讨论】:

  • 在这种特殊情况下,您可以使用if (sb?.Equals(null) ?? true),但这肯定比if (sb == null) 更麻烦且可读性更低。
  • 重载Object.Equals(Object obj) 的官方建议提到明确返回false 用于nullobj。它还声明了实现不应该抛出异常。无论如何,你不能保证每个人都遵循这个明智的建议! docs.microsoft.com/en-us/dotnet/api/system.object.equals
  • 还有静态 Equals 函数 object.Equals(object,object) 可以接受空对象。例如:if (object.Equals(sb, null) {..},或简称为if (Equals(sb, null)) {..}
【解决方案4】:

== 通常是“身份”等于意味着“对象 a 实际上与对象 b 在内存中是完全相同的对象”。

equals() 表示对象在逻辑上相等(例如,从业务角度来看)。因此,如果您要比较用户定义类的实例,如果您希望像 Hashtable 这样的东西正常工作,您通常需要使用和定义 equals()。

如果您有众所周知的具有“名称”和“地址”属性的 Person 类,并且您想将此 Person 用作包含有关它们的更多信息的 Hashtable 的键,则需要实现 equals()(和哈希)这样您就可以创建一个 Person 的实例并将其用作 Hashtable 的键来获取信息。

单独使用 ==,您的新实例将相同。

【讨论】:

    【解决方案5】:

    Equals== 都可以重载,因此调用其中一个或另一个的确切结果会有所不同。请注意,== 是在编译时确定的,因此虽然实际实现可能会发生变化,但使用的 == 在编译时是固定的,与 Equals 不同,它可以根据左侧的运行时类型使用不同的实现一边。

    例如string== 执行相等性测试。

    还要注意两者的语义可以是complex

    最佳实践是像这个例子那样实现相等。请注意,您可以根据您计划如何使用您的课程来简化或排除所有这些,并且structs 已经掌握了大部分内容。

    class ClassName
    {
        public bool Equals(ClassName other)
        {
            if (other == null)
            {
                return false;
            }
            else
            {
                //Do your equality test here.
            }
        }
    
        public override bool Equals(object obj)
        {
            ClassName other = obj as null; //Null and non-ClassName objects will both become null
            if (obj == null)
            {
                return false;
            }
            else
            {
                return Equals(other);
            }
        }
    
        public bool operator ==(ClassName left, ClassName right)
        {
            if (left == null)
            {
                return right == null;
            }
            else
            {
                return left.Equals(right);
            }
        }
    
        public bool operator !=(ClassName left, ClassName right)
        {
            if (left == null)
            {
                return right != null;
            }
            else
            {
                return !left.Equals(right);
            }
        }
    
        public override int GetHashCode()
        {
            //Return something useful here, typically all members shifted or XORed together works
        }
    }
    

    【讨论】:

    • 这并不完全正确。 == 被重载,而 Equals 被覆盖,这是一个重要的区别。因此,为 == 运算符执行的代码是在编译时链接的,而 Equals 是虚拟的,在执行时找到。
    • @StefanSteinegger:除非您正在编写库,否则您很少会遇到这种区别,我继续添加注释。
    • "== 被重载,而 Equals 被覆盖" -- Equals() 可以被重载 覆盖。我的意思是,显然System.Object.Equals(object) 只能被覆盖。但是没有什么能阻止一个类创建额外的 Equals() 方法重载,事实上,任何时候类实现 IEquatable<T> 都会发生这种情况。
    【解决方案6】:

    根据MSDN

    在 C# 中,有两种不同的相等性:引用相等(也称为身份)和值相等。值相等是一般理解的相等含义:它意味着两个对象包含相同的值。例如,两个值为 2 的整数值相等。引用相等意味着没有两个对象可以比较。相反,有两个对象引用,它们都引用同一个对象。

    ...

    默认情况下,运算符 == 通过确定两个引用是否指示同一个对象来测试引用相等性。

    【讨论】:

      【解决方案7】:

      要考虑的另一件事:如果您从另一种语言访问对象,== 运算符可能不可调用或可能具有不同的含义。通常,最好有一个可以按名称调用的替代方法。

      【讨论】:

        【解决方案8】:

        该示例是因为 DateTime 类实现了 IEquatable 接口,该接口实现了“用于确定实例相等性的特定于类型的方法”。根据MSDN.

        【讨论】:

          【解决方案9】:

          如果要表示比较的对象的内容应该相等,使用equals。将 == 用于原始值,或者如果您想检查被比较的对象是否是同一个对象。 For objects == 检查对象的地址指针是否相同。

          【讨论】:

            【解决方案10】:

            我见过 Object.ReferenceEquals() 用于想知道两个引用是否指向同一个对象的情况

            【讨论】:

              【解决方案11】:

              在大多数情况下,它们是相同的,因此为了清楚起见,您应该使用 ==。根据 Microsoft 框架设计指南:

              务必确保 Object.Equals 和相等运算符具有完全相同的语义和相似的性能特征。” https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/equality-operators

              但有时,有人会在不提供相等运算符的情况下覆盖 Object.Equals。在这种情况下,您应该使用 Equals 来测试值是否相等,并使用 Object.ReferenceEquals 来测试引用是否相等。

              【讨论】:

                【解决方案12】:

                如果你对Object进行反汇编(例如通过dotPeek),那么

                public virtual bool Equals(Object obj)
                

                描述为:

                // Returns a boolean indicating if the passed in object obj is
                // Equal to this.  Equality is defined as object equality for reference
                // types and bitwise equality for value types using a loader trick to
                // replace Equals with EqualsValue for value types). 
                //
                

                所以,取决于类型。 例如:

                        Object o1 = "vvv";
                        Object o2 = "vvv";
                        bool b = o1.Equals(o2);
                
                        o1 = 555;
                        o2 = 555;
                        b = o1.Equals(o2);
                
                        o1 = new List<int> { 1, 2, 3 };
                        o2 = new List<int> { 1, 2, 3 };
                        b = o1.Equals(o2);
                

                第一次 b 为真(对值类型执行相同),第二次 b 为真(对值类型执行相同),第三次 b 为假(对引用类型执行相同)。

                【讨论】:

                  猜你喜欢
                  • 2021-03-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-17
                  • 2016-02-01
                  • 2012-03-20
                  • 2015-03-17
                  • 1970-01-01
                  • 2012-06-22
                  相关资源
                  最近更新 更多