面向对象编程两个重要的相等概念:值相等和引用相等

System.Object类

  C#中所有的类都直接或间接继承自System.Object类,这使得C#中的类得以单根继承。如果我们没有明确指定继承类,编译器缺省认为该类继承自System.Object类。System.Object类也可用小写的object关键字表示,两者完全等同。下面是System.Object类的代码:

 
[Serializable, ClassInterface(ClassInterfaceType.AutoDual)]
public   class  Object
{
    
// Methods
    public Object();
    
public virtual bool Equals(object obj);
    
public static bool Equals(object objA, object objB);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
private extern Type FastGetExistingType();
    
private void FieldGetter(string typeName, string fieldName, ref object val);
    
private void FieldSetter(string typeName, string fieldName, object val);
    
protected override void Finalize();
    
private FieldInfo GetFieldInfo(string typeName, string fieldName);
    
public virtual int GetHashCode();
    
public Type GetType();
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal static extern bool InternalEquals(object objA, object objB);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
internal static extern int InternalGetHashCode(object obj);
    [MethodImpl(MethodImplOptions.InternalCall)]
    
private extern Type InternalGetType();
    [MethodImpl(MethodImplOptions.InternalCall)]
    
protected extern object MemberwiseClone();
    
public static bool ReferenceEquals(object objA, object objB);
    
public virtual string ToString();
}

  我们先看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
    }

}

编译程序并运行我们会得到以下输出:

TrueFalse
FalseFalseFalse
TrueTrueTrue
FalseTrueFalse
TrueTrueTrue
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值