C# 对象比较

 最近工作中接触到这个,有点迷糊。

 

.Net 中主要有四种相等比较,分别是:

  • ==操作符、
  • Object.Equals方法、
  • Object.ReferenceEquals方法、
  • 对象实例的Equals方法。

 

Object 的 Equals 静态方法实际上是对实例Equals方法的扩展,

增加了 null 的判断,适用于比较两个可能为空引用的对象。

对于值类型,和 Equals 实例方法完全一样。

public static bool Equals(object objA, object objB)
{
  if (objA == objB)
  {
    return true;
  }
  if (objA != null && objB != null)
  {
    return objA.Equals(objB);
  }
  return false;
}

 

ReferenceEquals 方法是比较两个对象的引用是否相同,即栈上的地址是否一样

对于值类型没有意义,参数中若有值类型参数出现,必定返回false。

对于引用类型,如果方法结果为True,这个相等是最严格、最纯粹、如假包换的相等,说明这两个参数其实是同一个对象,当然无论用其他哪种相等比较方式,同样也应返回True。

public static bool ReferenceEquals(object objA, object objB)
{
    return objA == objB;
}

 

 

==,

上面,我们说两个Object静态方法区别在值类型和引用类型上,对于其他相等比较区别也主要在此。

一般情况下,不是所有,对于引用类型 == 和 ReferenceEquals 静态方法作用相同;

值类型在这里则有区分,对于一些原生值类型,如int,long,char等,==是直接比较其数值,而且不同类型间可以互相比较,比如int和char,'A’==65返回的是True;

而对于一般的Struct,如果没有在代码中定义==(也包括!=)操作符,是不能用==比较的。

引用类型也可以定义 == 操作符,覆盖CLR原生支持的比较。

最常见的是String类型,它就定义了==操作符,很合理地放宽了相等的条件,使得String类型像原生值类型一样按值比较String类的 == 操作符其实就是直接调用的被自己重写过Equals方法

String类是最常用也最特别的一个类,大部分面试都会问到String的特点,除了不可变和内存驻留机制外,其他主要特点就是相等的特殊性了。

 

public virtual bool Equals(object obj)
{
    return RuntimeHelpers.Equals(this, obj);
}

 

实例 Equals 方法,这是个 Virtual 方法。

定义并使用操作符固然方便,不过除了像String之类的特殊情况,引用类型让 == 保持默认规则是更好的选择,而让 Equals 方法实现业务上的“值”相等。

如果不覆写,Equals 方法也是比较对象的引用。

 


对于值类型,实现==操作像一个点缀,

而如果想实现相等比较操作,应该优先重写Equals方法(同样若要实现大小比较,应该优先实现 IComparable 接口,而不是实现比较操作符),

从 Object 继承的 Equals 方法用于值类型时,比较两个对象的所有字段,全相等才为True。

 

为什么一定要优先重写它?

因为所有 .Net Framework 键值集合,都是用Equals实例方法做比较的,

所以它实际上成了.Net中的“潜规则”,无论是原生类型、结构或类的实例,都应以Equals方法作为其标准的相等比较方式,包括我们自己实现的类型。

用实例方法的好处也可以理解,更灵活,我们可以添加一些重载的Equals方法,申明不同的比较前提条件。

与重写的默认Equals方法配合,构成一套完整的比较规则,以符合现实复杂多变的标准。

 

.Net Framework 为较为复杂的比较提供了一个接口 System.Collections.IEqualityComparer,并提供了内置的实现,

如 StringComparer、EqualityComparer 我们自己写的比较类也可以实现这个接口。

 


posted @ 2018-09-03 21:23  Aaxuan  阅读(5512)  评论(0编辑  收藏  举报