[C#] C#中对象相等性比较方法的区别
2008-06-03 13:41
369 查看
C#有一个令初学者迷惑的地方就是它提供了四种不同的方法来比较对象的相等性:ReferenceEquals()和Equals()的两个版本。再加上比较运算符==。这四种比较方式之间的细微差别,很容易让初学者感到迷茫。下面我们就来看看这四种方式的区别。
首先来看看四个方法对引用类型的比较:
ReferenceEquals()方法
ReferenceEquals()是一个静态方法,测试两个引用是否指向类的同一个实例,特别是两个引用是否包含内存中的相同地址。作为静态方法,它不能重写,所以只能使用System.Object实现。如果提供的两个引用指向同一个对象实例,ReferenceEquals()总是返回true,否则就返回false。但是它认为null等于null:
SomeClass x, y;
x = new SomeClass();
y = new SomeClass();
bool b1 = ReferenceEquals(null, null); //return true
bool b2 = ReferenceEquals(null, x); //return false
bool b3 = ReferenceEquals(x, y); //return false because x and y
//point to different objects
x=y;
bool b4=ReferenceEquals(x,y) //return true because x and y
//point to one objects
虚拟的Equals()方法
Equals()虚拟版本的System.Object实现也比较引用。但因为这个方法是虚拟的,所以可以在自己的类中重写它,以便按值来比较对象。特别是如果希望类的实例用作字典中的键,就需要重写这个方法,以比较值,否则,根据重写Object.GetHashCode()的方式,包含对象的字典类要么不工作,要么工作的效率非常低。在重写Equals()方法时要注意,重写的代码不会抛出异常。这是因为如果抛出异常,字典类就会出问题,可能会包含其他在内部调用这个方法的.NET基类。
静态的Equals()方法
Equals()的静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就提供了禁止抛出异常的额外保护。静态重载首先要检查它传送的引用是否为null。如果它们都是null,就返回false。如果两个引用都不指向null,它就调用Equals()的虚拟实例版本。这表示在重写Equals()的实例版本时,其效果相当于也重写了静态版本。
比较运算符==
最好将比较运算符看作是严格值比较和严格引用比较之间的中间选项。在大多数情况下,下面的代码:
bool b = (x == y); //x, y is object references
表示比较引用。但是,如果把一些类看作值,其含义就会比较直观。在这些情况下,最好重写比较运算符,以执行值的比较。显然它的一个例子是System.String类,Microsoft重写了这个运算符,比较字符串的内容,而不是它们的引用。
其次再看看值类型的相等比较
在进行值类型的相等比较时,采用与引用类型相同的规则:ReferenceEquals()用于比较引用,Equals()用于比较值,比较运算符可以看作是一个中间项。但最大的区别是值类型需要装箱,才能把它们转换为引用,才能对它们执行方法。另外,Microsoft已经在System.ValueType类中重载了实例方法Equals(),以便对值类型进行合适的相等测试。如果调用sA.Equals(sB),其中sA和sB是某个结构的实例,则根据sA和sB是否在其所有的字段中包含相同的值,而返回true或false。另一方面,在默认情况下,可以对自己的结构使用==的未重载版本。在表达式中使用(sA==sB)会导致一个编译错误,除非在代码中为结构提供了==的重载版本。
另外,ReferenceEquals()在应用于值类型时,总是返回false,因为为了调用这个方法,值类型需要装箱到对象中。即使使用下面的代码:
bool b = ReferenceEquals(v, v); //v is a variable of some value type
也会返回false,因为在转换每个参数时,v都会被单独装箱,这意味着会得到不同的引用。调用ReferenceEquals()来比较值类型实际上没有什么意义。
尽管System.ValueType提供的Equals()默认重载肯定足以应付绝大多数自定义的结构,但仍可以为自己的结构重写它,以提高性能。另外,如果值类型包含作为字段的引用类型,就需要重写Equals(),以便为这些字段提供合适的语义,因为Equals()的默认重写仅比较它们的地址
最后希望本文可以让朋友们了解C#中各种相等性比较方法的区别,以便在自己编程时有区别的使用。
首先来看看四个方法对引用类型的比较:
ReferenceEquals()方法
ReferenceEquals()是一个静态方法,测试两个引用是否指向类的同一个实例,特别是两个引用是否包含内存中的相同地址。作为静态方法,它不能重写,所以只能使用System.Object实现。如果提供的两个引用指向同一个对象实例,ReferenceEquals()总是返回true,否则就返回false。但是它认为null等于null:
SomeClass x, y;
x = new SomeClass();
y = new SomeClass();
bool b1 = ReferenceEquals(null, null); //return true
bool b2 = ReferenceEquals(null, x); //return false
bool b3 = ReferenceEquals(x, y); //return false because x and y
//point to different objects
x=y;
bool b4=ReferenceEquals(x,y) //return true because x and y
//point to one objects
虚拟的Equals()方法
Equals()虚拟版本的System.Object实现也比较引用。但因为这个方法是虚拟的,所以可以在自己的类中重写它,以便按值来比较对象。特别是如果希望类的实例用作字典中的键,就需要重写这个方法,以比较值,否则,根据重写Object.GetHashCode()的方式,包含对象的字典类要么不工作,要么工作的效率非常低。在重写Equals()方法时要注意,重写的代码不会抛出异常。这是因为如果抛出异常,字典类就会出问题,可能会包含其他在内部调用这个方法的.NET基类。
静态的Equals()方法
Equals()的静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就提供了禁止抛出异常的额外保护。静态重载首先要检查它传送的引用是否为null。如果它们都是null,就返回false。如果两个引用都不指向null,它就调用Equals()的虚拟实例版本。这表示在重写Equals()的实例版本时,其效果相当于也重写了静态版本。
比较运算符==
最好将比较运算符看作是严格值比较和严格引用比较之间的中间选项。在大多数情况下,下面的代码:
bool b = (x == y); //x, y is object references
表示比较引用。但是,如果把一些类看作值,其含义就会比较直观。在这些情况下,最好重写比较运算符,以执行值的比较。显然它的一个例子是System.String类,Microsoft重写了这个运算符,比较字符串的内容,而不是它们的引用。
其次再看看值类型的相等比较
在进行值类型的相等比较时,采用与引用类型相同的规则:ReferenceEquals()用于比较引用,Equals()用于比较值,比较运算符可以看作是一个中间项。但最大的区别是值类型需要装箱,才能把它们转换为引用,才能对它们执行方法。另外,Microsoft已经在System.ValueType类中重载了实例方法Equals(),以便对值类型进行合适的相等测试。如果调用sA.Equals(sB),其中sA和sB是某个结构的实例,则根据sA和sB是否在其所有的字段中包含相同的值,而返回true或false。另一方面,在默认情况下,可以对自己的结构使用==的未重载版本。在表达式中使用(sA==sB)会导致一个编译错误,除非在代码中为结构提供了==的重载版本。
另外,ReferenceEquals()在应用于值类型时,总是返回false,因为为了调用这个方法,值类型需要装箱到对象中。即使使用下面的代码:
bool b = ReferenceEquals(v, v); //v is a variable of some value type
也会返回false,因为在转换每个参数时,v都会被单独装箱,这意味着会得到不同的引用。调用ReferenceEquals()来比较值类型实际上没有什么意义。
尽管System.ValueType提供的Equals()默认重载肯定足以应付绝大多数自定义的结构,但仍可以为自己的结构重写它,以提高性能。另外,如果值类型包含作为字段的引用类型,就需要重写Equals(),以便为这些字段提供合适的语义,因为Equals()的默认重写仅比较它们的地址
最后希望本文可以让朋友们了解C#中各种相等性比较方法的区别,以便在自己编程时有区别的使用。
相关文章推荐
- C#中??和?分别是什么意思? 在ASP.NET开发中一些单词的标准缩写 C#SESSION丢失问题的解决办法 在C#中INTERFACE与ABSTRACT CLASS的区别 SQL命令语句小技巧 JQUERY判断CHECKBOX是否选中三种方法 JS中!=、==、!==、===的用法和区别 在对象比较中,对象相等和对象一致分别指的是什么?
- [导入]C#中对象相等性比较方法的区别
- C#使用Equals()方法比较两个对象是否相等的方法
- C#中静态方法与非静态方法区别比较
- C# Distinct使用,支持对象的相等比较
- Java中两个对象的比较 equals()方法和==号的区别
- .Net(C#)中,对象的相等比较问题
- 深入java的equals方法,怎么比较两个对象的内容是否相等?对象的内容指的是什么?
- c#比较两个字符串相等的方法。
- c#中的3种对象比较方法
- Java进阶:深入equals方法对象相等比较
- .Net(C#)中,对象的相等比较问题
- setTimeout和setInterval的区别及用法、C#中遍历Hashtable对象[哈希表]的3种方法【遍历Hashtable】
- setTimeout和setInterval的区别及用法、C#中遍历Hashtable对象[哈希表]的3种方法
- JavaScript比较两个对象是否相等的方法
- java比较两个对象是否相等的方法
- .Net(C#)中,对象的相等比较问题
- C#的对象相等比较问题
- [原创]详述IComparer,IComparable接口,实现自定义方法比较对象大小并排序(C#)
- C#学习,对象的相等比较