C#中的相等有两种类型:引用相等(ReferenceEquals)和值相等(Equals)。值相等就是说两个对象包含相同的值。而引用相等则比较的是两个对象的引用是否是同一个对象。也就是说,如果ReferenceEquals为True,则Equals必然为True,反过来就不一定了。
引用相等(ReferenceEquals)在object类静态实现,其原型是public static bool ReferenceEquals(object objA, object objB)。针对值类型,首先是将值类型装箱变为引用类型(地址肯定不同),在比较地址是否相等,因此返回的总是false,所以该函数主要针对引用类型比较。
object类还实现另外两个Equal函数,分别为 public static bool Equals(object objA, object objB),public virtual bool Equals(object obj),针对值类型,没什么可说的,其它值一样就认为是相等的。对于引用类型,先看下面的例子:
public class test
{
int value = 1;
}
class Program
{
static void Main(string[] args)
{
test a = new test();
test b = new test();
Console.WriteLine(a.Equals(b));
Console.WriteLine(test.Equals(a,b));
Console.WriteLine(b.Equals(a));
Console.WriteLine(a==b);
Console.ReadKey();
}
}
返回的全是false,所以针对引用类型,比较的时地址是否一致。
对于自定义的引用类型,如果想要判断值相等,则必须重载Equals(object obj),.NET库中字符串就是重载了该函数,如:
string strA = "Hello"; string strB = string.Copy(strA); Console.WriteLine(strA == strB); //True Console.WriteLine(strA.Equals(strB)); //True Console.WriteLine(object.Equals(strA, strB)); //True Console.WriteLine(object.ReferenceEquals(strA, strB)); //False
先不考虑==号,看几个Equals的表现,Equals是要实现值相等比较的效果的,.net 在实现string的时候就重写了Equals(object) ,并添加重载函数Equals(string) ,因此两个比较才会返回True。而不实现这两个函数的后果就是都返回False,无法符合Equals函数应有的作用。
MSDN上Equals函数实现的保证:
Equals 的新实现应该遵循 Equals 的所有保证:
- x.Equals(x) 返回 true。
- x.Equals(y) 与 y.Equals(x) 返回相同的值。
- 如果 (x.Equals(y) && y.Equals(z)) 返回 true,则 x.Equals(z) 返回 true。
- 只要不修改 x 和 y 所引用的对象,x.Equals(y) 的后续调用就返回相同的值。
- x.Equals (null) 返回 false(仅非空值类型。有关更多信息,请参见可空类型(C# 编程指南)。)
由此看来,对于之前我的需求:比较两个自定义类的内容是否相同,还是应该重写Equals(Object),并建议重载对自己的类的实现Equals(MyClass)。
而对于==运算符的解释为:拖过判断两个引用是否指示同一对象来测试是否相等。也就是引用相等了,可对于上面对string的测试,RefrenceEquals为False时,==却返回的True,这是因为还有这么一条:当类型不可变(即实例中包含的数据不可更改)时,通过重载运算符== 来比较值是否相等而不是比较引用是否相等可能会很有用。(这里和java不同,java的==就是用来比较引用是否相等的,而且java也不允许算符重载)。
我们大部分情况下写的自定义类都是可变的,所以一般用不到也不应该对==进行重载。