学习C#语言时,经常会遇到继承System.Object类问题,这里将介绍继承System.Object类问题的解决方法。
继承System.Object类
C#中所有的类都直接或间接继承System.Object类,这使得C#中的类得以单根继承。如果我们没有明确指定继承类,编译器缺省认为该类继承System.Object类。System.Object类也可用小写的object关键字表示,两者完全等同。自然C#中所有的类都继承System.Object类的公共接口,剖析它们对我们理解并掌握C#中类的行为非常重要。下面是仅用接口形式表示的System.Object类:
- namespace System
- {
- public class Object
- {
- public static bool Equals(object objA,object objB){}
- public static bool ReferenceEquals(object objA,object objB){}
- public Object(){}
- public virtual bool Equals(object obj){}
- public virtual int GetHashCode(){}
- public Type GetType(){}
- public virtual string ToString(){}
- protected virtual void Finalize(){}
- protected object MemberwiseClone(){}
- }
我们先看object的两个静态方法Equals(object objA,object objB),ReferenceEquals(object objA,object objB)和一个实例方法Equals(object obj)。在我们阐述这两个方法之前我们首先要清楚面向对象编程两个重要的相等概念:值相等和引用相等。值相等的意思是它们的数据成员按内存位分别相等。引用相等则是指它们指向同一个内存地址,或者说它们的对象句柄相等。引用相等必然推出值相等。对于值类型关系等号“= =”判断两者是否值相等(结构类型和枚举类型没有定义关系等号“= =”,我们必须自己定义)。对于引用类型关系等号“= =”判断两者是否引用相等。值类型在C#里通常没有引用相等的表示,只有在非托管编程中采用取地址符“&”来间接判断二者的地址是否相等。
静态方法Equals(object objA,object objB)首先检查两个对象objA和objB是否都为null,如果是则返回true,否则进行objA.Equals(objB)调用并返回其值。问题归结到实例方法Equals(object obj)。该方法缺省的实现其实就是{return this= =obj;}也就是判断两个对象是否引用相等。但我们注意到该方法是一个虚方法,C#推荐我们重写此方法来判断两个对象是否值相等。实际上Microsoft.NET框架类库内提供的许多类型都重写了该方法,如:System.String(string),System.Int32(int)等,但也有些类型并没有重写该方法如:System.Array 等,我们在使用时一定要注意。对于引用类型,如果没有重写实例方法Equals(object obj),我们对它的调用相当于this= =obj,即引用相等判断。所有的值类型(隐含继承自System.ValueType类)都重写了实例方法Equals(object obj)来判断是否值相等。
注意对于对象x,x.Equals(null)返回false,这里x显然不能为null(否则不能完成Equals()调用,系统抛出空引用错误)。从这里我们也可看出设计静态方法Equals(object objA,object objB)的原因了--如果两个对象objA 和objB都可能为null,我们便只能用object. Equals(object objA,object objB)来判断它们是否值相等了--当然如果我们没有改写实例方法Equals(object obj),我们得到的仍是引用相等的结果。我们可以实现接口IComparable(有关接口我们将在“第七讲 接口继承与多态”里阐述)来强制改写实例方法Equals(object obj)。
对于值类型,实例方法Equals(object obj)应该和关系等号“= =”的返回值一致,也就是说如果我们重写了实例方法Equals(object obj),我们也应该重载或定义关系等号“= =”操作符,反之亦然。虽然值类型(继承自System.ValueType类)都重写了实例方法Equals(object obj),但C#推荐我们重写自己的值类型的实例方法Equals(object obj),因为系统的System.ValueType类重写的很低效。对于引用类型我们应该重写实例方法Equals(object obj)来表达值相等,一般不应该重载关系等号“= =”操作符,因为它的缺省语义是判断引用相等。
静态方法ReferenceEquals(object objA,object objB)判断两个对象是否引用相等。如果两个对象为引用类型,那么它的语义和没有重载的关系等号“= =”操作符相同。如果两个对象为值类型,那么它的返回值一定是false。
实例方法GetHashCode()为相应的类型提供哈希(hash)码值,应用于哈希算法或哈希表中。需要注意的是如果我们重写了某类型的实例方法Equals(object obj),我们也应该重写实例方法GetHashCode()--这理所应当,两个对象的值相等,它们的哈希码也应该相等。下面的代码是对前面几个方法的一个很好的示例:
- using System;
- struct A
- {
- public int count;
- }
- class B
- {
- public int number;
- }
- class C
- {
- public int integer=0;
- public override bool Equals(object obj)
- {
- C c=obj as C;
- if (c!=null)
- return this.integer==c.integer;
- else
- return false;
- }
- public override int GetHashCode()
- {
- return 2^integer;
- }
- }
- class Test
- {
- public static void Main()
- {
- A a1,a2;
- a1.count=10;
- a2=a1;
- //Console.Write(a1==a2);没有定义“= =”操作符
- Console.Write(a1.Equals(a2));//True
- Console.WriteLine(object.ReferenceEquals(a1,a2));//False
- B b1=new B();
- B b2=new B();
- b1.number=10;
- b2.number=10;
- Console.Write(b1==b2);//False
- Console.Write(b1.Equals(b2));//False
- Console.WriteLine(object.ReferenceEquals(b1,b2));//False
- b2=b1;
- Console.Write(b1==b2);//True
- Console.Write(b1.Equals(b2));//True
- Console.WriteLine(object.ReferenceEquals(b1,b2));//True
- C c1=new C();
- C c2=new C();
- c1.integer=10;
- c2.integer=10;
- Console.Write(c1==c2);//False
- Console.Write(c1.Equals(c2));//True
- Console.WriteLine(object.ReferenceEquals(c1,c2));//False
- c2=c1;
- Console.Write(c1==c2);//True
- Console.Write(c1.Equals(c2));//True
- Console.WriteLine(object.ReferenceEquals(c1,c2));//True
- }
- }
【编辑推荐】