【问题标题】:Comparing Two Objects比较两个对象
【发布时间】:2011-03-03 17:00:20
【问题描述】:

使用 Object.Equals 会导致两个使用相同构造函数的新创建对象的错误。为什么是这样?我希望能够比较两个对象并查看它们是否相同,换句话说,我想做与上面相同的测试,但我希望能够在第一次测试中得到一个真(因为这两个对象应该是相同的)。

如何在不为任何特定类构建自己的比较方法的情况下完成此操作。

using System;
class Program
{
    static void Main(string[] args)
    {
        Object Obj1 = new Object();
        Object Obj2 = new Object();
        Console.WriteLine(Obj1.Equals(Obj2));
        Obj2 = Obj1;
        Console.WriteLine(Obj1.Equals(Obj2)); 
    }
}

/* This example produces the following output:
False
True
 */

附录:

Object o1 = new Object();
Object o2 = new Object();

o1.value1 = 1;
o2.value1 = 1;

//Now I want to compare o1 and o2 and see if they are equal
//Can I do this without building my own function?

【问题讨论】:

  • 我建议你看看documentation for Object.Equals 来了解这个方法的默认行为。
  • 我现在明白了 Object.Equals 的作用,您能帮我找出原始帖子附录中的问题吗?谢谢!
  • 您的附录无法编译。你在谈论System.Object类型,对吧?此类型没有任何 value1 字段或属性。

标签: c# object


【解决方案1】:

Object.Equals() 使用引用相等进行比较,只有当它是相同的对象引用时才会导致 true。您可以为自己的派生自 Object 的类型覆盖此行为。

来自 MSDN:

Equals 的默认实现 支持引用相等 引用类型和按位相等 对于值类型。引用相等 表示对象引用 比较指的是同一个对象。 按位相等意味着对象 被比较的具有相同的二进制 表示。

请注意,派生类型可能 覆盖 Equals 方法以 实现价值平等。价值 相等表示比较的对象 即使它们具有相同的值 有不同的二进制表示。 例如,考虑两个 Decimal 代表数字的对象 1.10 和 1.1000。 Decimal 对象没有按位相等,因为 他们有不同的二进制 陈述来解释 不同数量的尾随零。 然而,这些物品是有价值的 平等,因为数字 1.10 和 1.1000 被认为是相等的,因为尾随 零是微不足道的。

【讨论】:

    【解决方案2】:

    我做这个答案的时候很着急,所以我重写了我的答案。

    equals 方法检查两个对象的引用是否相等,当您使用相同的数据创建同一个类两次时,它们仍然存储在内存中的不同位置,因此它们的引用不相等,而 Equals 方法返回 false。

    要检查类的值是否相等,您必须重写 Equals 方法,还要使用 == 和 != 运算符,我们需要重载它们。

    例如,字符串类会覆盖它,如果它不这样做,则会打印 false:

    string test = "string";
    string test2 = "string";
    Console.WriteLine(test == test2);
    

    例子:

    // Example 1:
    // Without overriding the Equals method:
    Foo f1 = new Foo();
    Foo f2 = new Foo();
    f1.Name = "String";
    f2.Name = "String";
    bool fooEquals = f1 == f2; // False
    f2.Name = "Test";
    fooEquals f1 == f2; // False
    
    // Example 2:
    // With overriding the Equals method:
    Foo f1 = new Foo();
    Foo f2 = new Foo();
    f1.Name = "String";
    f2.Name = "String";
    bool fooEquals = f1 == f2; // True
    f2.Name = "Test";
    fooEquals f1 == f2; // False
    

    这是第一个例子中的 Foo:

    public class Foo
    {
        public string Name { get; set; }
    }
    

    这是第二个例子中的 Foo:

    public class Foo
    {
        public string Name { get; set; }
    
        public override bool Equals(object obj)
        {
            // If parameter is null return false.
            if (obj == null)
            {
                return false;
            }
    
            // If parameter cannot be cast to Point return false.
            Foo p = obj as Foo;
            if ((System.Object)p == null)
            {
                return false;
            }
    
            // Return true if the fields match:
            return (Name == p.Name);
        }
    
        public bool Equals(Foo p)
        {
            // If parameter is null return false:
            if ((object)p == null)
            {
                return false;
            }
    
            // Return true if the fields match:
            return (Name == p.Name);
        }
    
        public static bool operator ==(Foo f1, Foo f2)
        {
            return f1.Equals(f2);
        }
    
        public static bool operator !=(Foo f1, Foo f2)
        {
            return !f1.Equals(f2);
        }
    
        public override int GetHashCode()
        {
            return Name.GetHashCode();
        }
    }
    

    注意:在覆盖 Equals 或重载 ==/!= 时,Visual Studio 需要您覆盖 GetHashCode 函数。对于 Foo 的每个实例,该函数应该是唯一的,并且具有相同的值。

    【讨论】:

    • 用我自己的方法覆盖 == 运算符?所以没有内置的东西可以比较两个对象并检查非引用相等性?
    • @sooprise 它怎么知道如何比较它们?
    • 你不能 override 操作符,只能 overload 它们......在这种情况下 == 无论如何都不会被调用,只有等于方法。它们不是一回事。
    • 首先这不是Java,第二个Equals方法与==操作符相同。好吧,您实际上并没有覆盖运算符,也没有重载,而是覆盖了运算符调用的方法。
    • 安德鲁,这正是我想知道的,是否有一个足够聪明的内置方法来做到这一点?但从你的帖子看来它不够聪明:(
    【解决方案3】:

    大部分答案都是正确的,我只是提供更多细节:

    从 System.Object 派生的类的任何实例(System.ValueType 类除外)都是“引用”类。 “参考”类有两个部分;实例的“肉”被放置在程序的内存中,称为“堆”,然后指向该内存地址的“引用”或“指针”被放置在堆栈中。有关什么是堆栈和堆以及它们为何必要的更多信息,请参阅 MSDN 文档或计算机编程的基本入门。

    在 .NET 中,默认比较引用类型的两个变量是否相等是比较变量存储的内存地址。这是“参考平等”;如果两个变量指向同一个内存地址,它们是相等的。如果不是,他们不是。

    对于“语义”或“结构”相等,需要更深入地了解每个变量的内存空间中的内容。这在一般情况下是做不到的;开发人员必须为他希望比较的每个类定义是什么使他的类的两个实例在语义上相等。这是通过重写从 Object 类继承的 Equals() 方法来实现的(因此对所有 .NET 类都是通用的)。 Equals 覆盖通常具有以下结构:

    public override bool Equals (object other)
    {
        //Reference equality is still a simple and necessary check
        if(Object.ReferenceEquals(this, other)) return true;
    
        //Another simple check; the two objects should be the same type
        if(this.GetType() != other.GetType()) return false;
    
        //Now, compare members of the object that you want to use to determine
        //"semantic" equality. These members, if also reference types, must also
        //be "semantically" equal.
    
        if(this.Property1 == other.Property1 && this.FieldA == other.FieldA && ... )
           return true;
    
        return false;
    }
    

    实现 IComparable、IEquatable 和 IStructuralEquatable 接口为消费者提供了线索,即当前类的作者具有相等或其他比较的自定义定义。它们在某些情况下是必要的,但并非完全如此。

    【讨论】:

      【解决方案4】:

      我相信你必须创建自己的比较方法,并实现 ICompare 接口

      【讨论】:

        【解决方案5】:

        Object.Equals() 是引用相等。您必须构建自己的比较方法,无论是覆盖 equals 还是实现 IEqualityComparer 或任何其他比较接口。

        【讨论】:

          【解决方案6】:

          对象是使用对对象的引用进行比较的,这与按值比较的基元不同。这经常困扰着新手,字符串在很多语言中都是对象,他们只是尝试使用原始比较来检查它们的相等性。

          如果您真的希望能够使用== 运算符进行比较,那么您可以overload the operator。

          【讨论】:

            【解决方案7】:

            object 类型除了引用标识之外绝对没有显着特征。如果您希望两个不同的object 实例比较相等,只需实现您自己的比较器:

            class ObjectComparer : IEqualityComparer<object>
            {
                public bool Equals(object x, object y)
                {
                    if (x == null || y == null)
                    {
                        return false;
                    }
            
                    return x.GetType == typeof(object) && y.GetType() == typeof(object);
                }
            
                public int GetHashCode(object obj)
                {
                    if (obj == null)
                    {
                        return 0;
                    }
            
                    if (obj.GetType() == typeof(object))
                    {
                        return 1; // I don't know, whatever.
                    }
            
                    return obj.GetHashCode();
                }
            }
            

            哦等等,那是你不想想要做的。好吧,然后创建你自己的类型,按照你想要的方式运行:

            sealed class SimpleObject()
            {
                // No characteristics, all instances equal.
                public override bool Equals(object obj)
                {
                    return obj is SimpleObject;
                }
            
                public override int GetHashCode()
                {
                    return 0;
                }
            }
            

            您不能只更改 object 类型本身。

            【讨论】:

            • @ilivewithian:什么?问题标记为 C#!代码也是C#! (注意using 声明。)
            • 是的,不知道那里发生了什么。我添加了该评论作为对 equals 方法大小写评论的回复。加上一个错字,我的目标是“它是”。不知道为什么会在这里结束。
            【解决方案8】:

            对象类的equals方法比较两个引用。由于您正在创建一个新对象,因此它们永远不会相等。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-08-02
              • 2014-01-26
              • 2012-08-17
              • 2014-03-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多