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