自定义实现两个对象的相等比较,一种方案是重写Object类的Equals方法,很easy,如果相等返回true,不相等就返回false。不过,如果把自定义相等的比较用于泛型集,比如Dictionary、HashSet等,这些集合都有一个共同点——必须标识存储项的唯一性,即每一个子项都有对应的key。

object.Equals方法是面向Object类型的,如果用于泛型对象,在判断是否相等的过程需要进行大量的装箱/拆箱操作,尤其是复合类型,由于要进行细致的比较,类型转换更为频繁,这样会带来一定量的性能开销,所以,对于泛型集合的相等比较,应该考虑使用 IEqualityComparer<T>,Dotnet类型提供了一个实现了该接口的抽象类——EqualityComparer<T>。

在实际使用中,不妨直接实现这个抽象类,好处是该抽象类公开了一个静态的Default属性,可以返回平台默认的比较方案。因此,实现该抽象类的好处在于,既可以提供自定义实现,同时也可以保留默认行为。

我们先来解释一下,为什么在泛型集合中需要用到自定义相等比较。看例子,咱们以常用的Dictionary为例,字典的Key我用一个叫Entity的类来标识,该类定义如下。

    public class Entity
{
public int ID { get; set; }
public string Name { get; set; }
}

然后,我们实例化一个字典,并向其中添加两个项。

            IDictionary<Entity, string> dic = new Dictionary<Entity, string>();

            dic.Add(new Entity { ID = , Name = "小明" }, "C++");
dic.Add(new Entity { ID = , Name = "小王" }, "VB");

接着,从字典中读出Key为ID = 2 , Name = "小王" 的值。

            Entity findkey = new Entity
{
ID = ,
Name = "小王"
}; if (dic.ContainsKey(findkey))
{
Console.WriteLine(dic[findkey]);
}

在查找时,先实例化一个Entity,然后给ID和Name属性赋上要查找的值,随后调用字典实例的ContainsKey方法判断一下要查找的key是否存在于字典中,如果存在,就输出该key对应的值。

代码看起来很完美,但一旦运行,你会发现什么都没找到。为啥呢?

因为该字典存储项的key是我们自定义的Entity类,当我们要从中查找时,是另外实例化了一个Entity对象,并赋了对应的属性值去查找的,可是问题就来了,我们实例化的findkey对象,与存入到字典中的Entity不是同一个对象,虽然它们的属性值相等,但它们引用的不是同一个实例,因为被判定为不相等的对象,故找不到对应的Key。

这个时候,EqualityComparer就派上用场了,自定义一个类并从它派生,添加自己的代码实现,不管是不是同一个对象实例,只要属性的值相等,则视为相同的key。

    public sealed class CustomEqComparer : EqualityComparer<Entity>
{
public override bool Equals(Entity x, Entity y)
{
if (x.ID == y.ID && x.Name == y.Name)
return true;
return false;
} public override int GetHashCode(Entity obj)
{
return obj.ID.GetHashCode();
}
}

实现Equals方法,如果两个对象相等,返回真,否则返回假。GetHashCode方法返回哈希值,算法不应该过于复杂,避免性能开销,只要能够保证相等的两个对象返回相同的哈希值;不相等的对象返回不同的哈希值,这样就可以了。这里直接以ID属性的值为哈希,所以,每个key的ID值不能重复。

自定义完比较器后,只要在实例化字典实例时传给它的构造函数就可以了。把上面的代码改为:

            CustomEqComparer comp = new CustomEqComparer();
IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp);

现在,再次执行前面的例子,ID=2,Name="小王"的key就可以查找出来了。

完整的演示代码如下:

    class Program
{
static void Main(string[] args)
{
CustomEqComparer comp = new CustomEqComparer();
IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp); dic.Add(new Entity { ID = , Name = "小明" }, "C++");
dic.Add(new Entity { ID = , Name = "小王" }, "VB"); Entity findkey = new Entity
{
ID = ,
Name = "小王"
}; if (dic.ContainsKey(findkey))
{
Console.WriteLine(dic[findkey]);
} Console.Read();
}
} public class Entity
{
public int ID { get; set; }
public string Name { get; set; }
} public sealed class CustomEqComparer : EqualityComparer<Entity>
{
public override bool Equals(Entity x, Entity y)
{
if (x.ID == y.ID && x.Name == y.Name)
return true;
return false;
} public override int GetHashCode(Entity obj)
{
return obj.ID.GetHashCode();
}
}

【.net 深呼吸】EqualityComparer——自定义相等比较的更多相关文章

  1. 【.net 深呼吸】自定义特性(Attribute)的实现与检索方法

    在.net的各个语言中,尤其是VB.NET和C#,都有特性这一东东,具体的概念,大家可以网上查,这里老周说一个非标准的概念——特性者,就是对象的附加数据.对象自然可以是类型.类型成员,以及程序集. 说 ...

  2. 【.net 深呼吸】自定义缓存配置(非Web项目)

    在前一篇烂文中,老周简单讲述了非Web应用的缓存技术的基本用法.其实嘛,使用系统默认方案已经满足我们的需求了,不过,如果你真想自己来配置缓存,也是可以的. 缓存的自定义配置可以有两种方案,一种是用代码 ...

  3. 【.net 深呼吸】自定义应用程序配置节

    实际上,应用程序配置文件 App.config,是由各个节(Configuration Section)组成的,通常,配置节是按功能划分的,比如我们很熟悉的 appSettings.connectio ...

  4. 【.NET深呼吸】基础:自定义类型转换

    照例,老周在开始吹牛之前,先讲讲小故事,这是朋友提出的建议,老TMD写技术有什么了不起的,人人都会写.后来老周想想,也确实,代码谁不会写,能写到有品位有感悟,就不容易做到.于是,老周接受了该朋友的建议 ...

  5. C#集合 -- 自定义集合与代理

    前面章节所讨论的集合都可以直接实例化,因此我们可以非常方便地使用这些集合类.但是如果你试图在集合添加或移除元素时添加控制,它们就不适用了.对于强类型集合,在某些情况下,你需要添加这样的控制: 添加或移 ...

  6. [C#] 类型学习笔记三:自定义值类型

    既前两篇之后,这一篇我们讨论通过struct 关键字自定义值类型. 在第一篇已经讨论过值类型的优势,节省空间,不会触发Gargage Collection等等. 在对性能要求比较高的场景下,通过str ...

  7. 自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧

    一:背景 1. 讲故事 曾今在项目中发现有同事自定义结构体的时候,居然没有重写Equals方法,比如下面这段代码: static void Main(string[] args) { var list ...

  8. 栈(Stack) --- C# 自定义和微软官方的区别

    最近在学习算法基础,本篇文章作为一个记录,也算是一次实践和总结.(顺便也深入C#运行时学习一下) 目录 1. 栈是什么 2. Stack 自定义实现 3. Stack C#官方实现 4. 区别 5. ...

  9. 【.net 深呼吸】细说CodeDom(7):索引器

    在开始正题之前,先补充一点前面的内容. 在方法中,如果要引用方法参数,前面的示例中,老周使用的是 CodeVariableReferenceExpression 类,它用于引用变量,也适用于引用方法参 ...

随机推荐

  1. ASP.NET Aries 入门开发教程6:列表数据表格的格式化处理及行内编辑

    前言: 为了赶进度,周末也写文了! 前几篇讲完查询框和工具栏,这节讲表格数据相关的操作. 先看一下列表: 接下来我们有很多事情可以做. 1:格式化 - 键值的翻译 对于“启用”列,已经配置了格式化 # ...

  2. ExtJS 4.2 业务开发(一)主页搭建

    本篇开始搭建一个ExtJS 4.2单页面应用, 这里先介绍主页的搭建,内容包括:主页结构说明.扩展功能等方面. 目录 1. 主页结构说明 2. 扩展功能 3. 在线演示 1. 主页结构说明 1.1 主 ...

  3. console的高级使用

    1.console.table()用来表格化展示数据. var people = { zqz: { name: 'zhaoqize', age: 'guess?' }, wdx: { name: 'w ...

  4. iOS开发之再探多线程编程:Grand Central Dispatch详解

    Swift3.0相关代码已在github上更新.之前关于iOS开发多线程的内容发布过一篇博客,其中介绍了NSThread.操作队列以及GCD,介绍的不够深入.今天就以GCD为主题来全面的总结一下GCD ...

  5. Java定时任务的常用实现

    Java的定时任务有以下几种常用的实现方式: 1)Timer 2)ScheduledThreadPoolExecutor 3)Spring中集成Cron Quartz 接下来依次介绍这几类具体实现的方 ...

  6. 利用Select2优化@Html.ListBoxFor显示,学会用MultiSelectList

    最近需要用到多选框,Asp.Net MVC自带的@Html.ListBox或@Html.ListBoxFor的显示效果太差,于是找到了Select2进行优化,并正式了解了多选框的操作方法. 首先介绍多 ...

  7. css_02之盒模型、渐变

    1.框模型:盒模型,①对象实际宽度=左右外边距+左右边框+左右内边距 + width:②对象实际高度=上下外边距+上下边框+上下内边距 + height: 2.外边距:margin:取值:①top(上 ...

  8. Android之SAX解析XML

    一.SAX解析方法介绍 SAX(Simple API for XML)是一个解析速度快并且占用内存少的XML解析器,非常适合用于Android等移动设备. SAX解析器是一种基于事件的解析器,事件驱动 ...

  9. Atitit.你这些项目不都是模板吗?不是原创  集成和整合的方式大总结

    Atitit.你这些项目不都是模板吗?不是原创  集成和整合的方式大总结 1.1. 乔布斯的名言:创新即整合(Creativity is just connecting things).1 1.2. ...

  10. Oracle创建表空间

    1.创建表空间 导出Oracle数据的指令:/orcl file=C:\jds.dmp owner=jds 导入Oracle数据的指令:imp zcl:/orcl file=C:\jds.dmp fu ...