首页 > 代码库 > 细说对象的相等性
细说对象的相等性
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_Equality和op_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