首页 > 代码库 > 细说对象的相等性

细说对象的相等性

1 从Object.Equals方法说起

使用Object.Equals方法可以确定两个Object实例是否相等。

Object.Equals方法具有以下重载形式:

(1)Object.Equals (Object) 

该方法可以确定指定的Object是否等于当前的Object。相等时返回true,否则返回false。

(2)Object.Equals (Object, Object) 

该方法可以确定指定的两个Object实例是否相等。相等时返回true,否则返回false。此方法为静态方法。

例1 定义Student类,并使用Object.Equals方法确定Student实例是否相等。

using System;
namespace IEquatableExp
{
    public class Student
    {
        private int studentID;
        private string studentName;
        private int age;
        public Student(int studentID, string studentName, int age)
        {
            this.studentID = studentID;
            this.studentName = studentName;
            this.age = age;
        }
        public int StudentID
        {
            get { return this.studentID;}
        }
        public string StudentName
        {
            get { return this.studentName; }
        }
        public int Age
        {
            get { return this.age; }
        }
    }
}
using System;
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student(1,"tiana",25);
            Student stu2 = stu1;
            Student stu3 = new Student(stu1.StudentID,stu1.StudentName,stu1.Age);
            Console.WriteLine("(Student)stu1, (Student)stu2,(Student)stu3: ");
            Console.WriteLine("stu1.Equals(stu2) = {0}", stu1.Equals(stu2));
            Console.WriteLine("stu1.Equals(stu3) = {0}", stu1.Equals(stu3));
            Console.WriteLine("Object.Equals(stu1, stu2) = {0}", Object.Equals(stu1, stu2));
            Console.WriteLine("Object.Equals(stu1, stu3) = {0}", Object.Equals(stu1, stu3));
        }
    }
}

代码运行结果如下图所示:

  

从例1的运行结果可以了解到:

Object.Equals方法的默认实现仅支持引用相等。

 

2 说说引用相等和值相等

对于引用类型,Object.Equals方法会去判断对象的引用是否指向的是同一个对象,若指向的是同一个对象则认为是引用相等

如例1中的引用stu1与stu2指向相同的对象,而引用stu3指向了另一个对象,所以调用Object.Equals方法确认stu1与stu2的相等性时会返回true,调用Object.Equals方法确认stu1与stu3的相等性时会返回false,尽管此时stu3指向的对象的值与stu1指向的对象的值相同。

对于值类型,Object.Equals方法是根据对象的值来确定对象的相等性的(MSDN上也称为按位相等)。此乃值相等也。

对于前面例子中的Student类,若修改其类型为struct,其他代码均不变,执行结果又会是怎样的呢。

using System;
namespace IEquatableExp
{
//修改Student的类型为struct
    public struct Student
    {
        //此部分代码不变
    }
}

修改代码后再次执行程序,得到以下结果。


因为struct是值类型,所以通过Object.Equals方法确定struct对象是否相等是根据对象的值来判断的,很显然例子中的stu1,stu2,stu3的值均相同,所以结果返回true。

 

3 C#中的值类型和引用类型

C#中,值类型包括:整型,长整型,浮点型,字符型,布尔型,枚举和结构体等。

引用类型包括:基类Object,字符串,用户自定义的类,接口和数组等。

 

4 再来看一个使用Object.Equals方法的例子

下面通过例2来进一步说明Object.Equals方法的使用。

例2 代码中使用Object.Equals方法对整型,字符串,可变字符字符串是否相等进行判断。

using System;
using System.Text;
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 32;
            int b = 32;
            Console.WriteLine("(int)a = {0}; (int)b = {1}:", a, b);
            Console.WriteLine("a.Equals(b) = {0}", a.Equals(b));
            Console.WriteLine("Object.Equals(a, b) = {0}", Object.Equals(a, b));
 
            Console.WriteLine();
 
            string str1 = "tiana";
            string str2 = "tiana";
            Console.WriteLine("(string)str1 = {0}; (string)str2 = {1}:", str1, str2);
            Console.WriteLine("str1.Equals(str2) = {0}", str1.Equals(str2));
            Console.WriteLine("Object.Equals(str1, str2) = {0}", Object.Equals(str1, str2));
 
            Console.WriteLine();
 
            StringBuilder sb1 = new StringBuilder("tiana");
            StringBuilder sb2 = new StringBuilder("tiana");
            Console.WriteLine("(StringBuilder)sb1 = {0}; (StringBuilder)sb2 = {1}:", sb1, sb2);
            Console.WriteLine("sb1.Equals(sb2) = {0}", sb1.Equals(sb2));
            Console.WriteLine("Object.Equals(sb1, sb2) = {0}", Object.Equals(sb1, sb2));
        }
    }
}

代码执行结果如下图所示:

 

对于整型,我们无需做过多说明,因为当整型对象的值相等时即认为对象相等。

对于字符串,尽管字符串是引用类型,但是字符串类String中已经对Object.Equals方法进行了重写,使字符串类支持值相等。

查看String类的源代码,你会发现String类提供了以下几个Equals方法的重载。

 

其中Equals(Object):Boolean方法便是对Object.Equals方法的重写。

对于可变字符字符串类StringBuilder,我们同样通过查看其源代码来分析结果产生的原因。

查看源代码可以了解到,StringBuilder类仅提供了自己的Equals方法,而并没有重写Object.Equals方法,所以实例中“sb1.Equals(sb2)”会调用StringBuilder类自己实现的Equals方法,该方法被实现成支持值相等,而“Object.Equals(sb1, sb2)”部分仍然会调用Object.Equals方法的实现,按照引用来确认对象是否相等。正因为此,所以会出现例子中的结果。

 

通过分析例2的运行情况,我们可以学习到:

Object.Equals的默认实现仅支持引用相等,但派生类可重写此方法以支持值相等。

例2中的String类与StringBuilder类可以很好的说明这点。

 

5 自娱自乐一下

下面给出一个很小的例子,供大家自娱自乐一下。

例3 大家知道下面代码的执行结果吗?要是知道的话,请大声说出来吧。

using System;
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 32;
            byte b = 32;
            Console.WriteLine("(int)a = {0}; (byte)b = {1}:", a, b);
            Console.WriteLine("a.Equals(b) = {0}", a.Equals(b));
            Console.WriteLine("a.Equals(b) = {0}", b.Equals(a));
         }
    }
}

不知道的话,那就直接看下面的结果。

 

为什么会这样?大家自己去琢磨吧。

  

6 在Student类中重写Object.Equals方法

接下来,我们要再次修改我们的Student类,在类中重写Object.Equals方法使其支持值相等。

例4 使我们的Student类支持值相等

using System;
namespace IEquatableExp
{
    public class Student
    {
        private int studentID;
        private string studentName;
        private int age;
        public Student(int studentID, string studentName, int age)
        {
            this.studentID = studentID;
            this.studentName = studentName;
            this.age = age;
        }
        public int StudentID
        {
            get { return this.studentID;}
        }
        public string StudentName
        {
            get { return this.studentName; }
        }
        public int Age
        {
            get { return this.age; }
        }
 
        public override bool Equals(Object otherObject)
        {
            if (otherObject == null)
            {
                return false;
            }
            Student otherStudent = otherObject as Student;
            if (otherStudent == null)
            {
                return false;
            }
            else
            {
                return (this.StudentName == otherStudent.StudentName)
                    && (this.StudentID == otherStudent.StudentID)
                    && (this.Age == otherStudent.Age);
            }
        }
 
        public override int GetHashCode()
        {
            return this.StudentID.GetHashCode();
        }
    }
}
using System;
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student(1, "tiana", 25);
            Student stu2 = stu1;
            Student stu3 = new Student(stu1.StudentID, stu1.StudentName, stu1.Age);
            Console.WriteLine("(Student)stu1, (Student)stu2, (Student)stu3: ");
            Console.WriteLine("stu1.Equals(stu2) = {0}", stu1.Equals(stu2));
            Console.WriteLine("stu1.Equals(stu3) = {0}", stu1.Equals(stu3));
            Console.WriteLine("Object.Equals(stu1, stu2) = {0}", Object.Equals(stu1, stu2));
            Console.WriteLine("Object.Equals(stu1, stu3) = {0}", Object.Equals(stu1, stu3));
        }
    }
}

运行程序,得到以下结果:

 

因为stu1,stu2,stu3的值相等,所以程序会返回true。

 

7 使我们的Student类实现IEquatable(T)接口

例5 实现IEquatable(T)接口

using System;
namespace IEquatableExp
{
    public class Student:IEquatable<Student>
    {
        private int studentID;
        private string studentName;
        private int age;
        public Student(int studentID, string studentName, int age)
        {
            this.studentID = studentID;
            this.studentName = studentName;
            this.age = age;
        }
        public int StudentID
        {
            get { return this.studentID;}
        }
        public string StudentName
        {
            get { return this.studentName; }
        }
        public int Age
        {
            get { return this.age; }
        }
 
        public bool Equals(Student otherStudent)
        {
            if (otherStudent == null)
            {
                return false;
            }
            return (this.StudentName == otherStudent.StudentName)
                    && (this.StudentID == otherStudent.StudentID)
                    && (this.Age == otherStudent.Age);
        }
 
        public override bool Equals(Object otherObject)
        {
            if (otherObject == null)
            {
                return false;
            }
            Student otherStudent = otherObject as Student;
            if (otherStudent == null)
            {
                return false;
            }
            else
            {
                return Equals(otherStudent);
            }
        }
 
        public override int GetHashCode()
        {
            return this.StudentID.GetHashCode();
        }
    }
}
using System;
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student(1, "tiana", 25);
            Student stu2 = stu1;
            Student stu3 = new Student(stu1.StudentID, stu1.StudentName, stu1.Age);
            Console.WriteLine("(Student)stu1, (Student)stu2,(Student)stu3: ");
            Console.WriteLine("stu1.Equals(stu2) = {0}", stu1.Equals(stu2));
            Console.WriteLine("stu1.Equals(stu3) = {0}", stu1.Equals(stu3));
            Console.WriteLine("Object.Equals(stu1, stu2) = {0}", Object.Equals(stu1, stu2));
            Console.WriteLine("Object.Equals(stu1, stu3) = {0}", Object.Equals(stu1, stu3));
        }
    }
}

代码运行结果如下图所示:

 

IEquatable(T)接口提供了Equals方法的定义,实现该接口的类需要提供Equals方法的实现,我们在Student类中实现了IEquatable(T)接口的Equals方法并重写了Object.Equals方法,使其均支持值相等。这样一来,可以使Object.Equals方法的行为与IEquatable<T>.Equals方法的行为保持一致。

至于程序的运行结果我就不做过多解释了,相信大家都能看得懂。

 

8 重载op_Equality和op_Inequality运算符

最后,在我们的Student类中,重载op_Equalityop_Inequality运算符

例6 此实例仅包含部分代码,下面所给代码是在例5的Student类中新增的代码

public static bool operator ==(Student student1, Student student2)
{
     if ((object)student1 == null || (object)student2 == null)
     {
          return Object.Equals(student1, student2);
     }
     return student1.Equals(student2);
}
 
public static bool operator !=(Student student1, Student student2)
{
     if ((object)student1 == null || (object)student2 == null)
     {
          return !Object.Equals(student1, student2);
     }
     return !student1.Equals(student2);
}
同时给出测试代码。

using System; 
namespace IEquatableExp
{
    class Program
    {
        static void Main(string[] args)
        {
            Student stu1 = new Student(1, "tiana", 25);
            Student stu2 = stu1;
            Student stu3 = new Student(stu1.StudentID, stu1.StudentName, stu1.Age);
            Console.WriteLine("(Student)stu1, (Student)stu2,(Student)stu3: ");
            Console.WriteLine("stu1 == stu2 = {0}", stu1 == stu2);
            Console.WriteLine("stu1 != stu3 = {0}", stu1 != stu3);
        }
    }
}

运行结果如下图所示:

 

到这里,我们便可以确保所有测试相等性返回的结果均保持一致。

 

好了,就到这里了。

88