自定义实现两个对象的相等比较,一种方案是重写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. Be Better:遇见更好的自己-2016年记

    其实并不能找到好的词语来形容过去的一年,感觉就如此平淡的过了!没有了毕业的稚气,看事情淡了,少了一丝浮躁,多了一分认真.2016也许就是那句话-多读书,多看报,少吃零食多睡觉,而我更愿意说--Be B ...

  2. 百度推出新技术 MIP,网页加载更快,广告呢?

    我们在2016年年初推出了MIP,帮助移动页面加速(原理).内测数据表明,MIP页面在1s内加载完成.现在已经有十多家网站加入MIP项目,有更多的网站正在加入中.在我们收到的反馈中,大部分都提到了广告 ...

  3. 在SQL2008查找某数据库中的列是否存在某个值

    在SQL2008查找某数据库中的列是否存在某个值 --SQL2008查找某数据库中的列是否存在某个值 create proc spFind_Column_In_DB ( @type int,--类型: ...

  4. 使用EF CodeFirst 创建数据库

    EntityFramework 在VS2015添加新建项时,选择数据->ADO.NET 实体数据模型,有一下选项 来自数据库的EF设计器,这个就是我们最常用的EntityFramework设计模 ...

  5. Android SDK 在线更新镜像服务器资源

    本文转自:http://blog.kuoruan.com/24.html.感谢原作者. 什么是Android SDK SDK:(software development kit)软件开发工具包.被软件 ...

  6. 真假4K电视验证:一张图足矣

    国庆期间笔者逛了一下电视卖场,考虑到国内电视台以及宽带的情况,1080P至少还能用十年,所以只想要个2k电视就够了.然而事与愿违,卖场中八成的都是4k电视,清一色的4k电视让人眼花缭乱.难道4k面板技 ...

  7. The Zen of Python

    Beautiful is better than ugly. 优美总比丑陋好Explicit is better than implicit. 直率总比含蓄好Simple is better than ...

  8. 【转】外部应用和drools-wb6.1集成解决方案

    一.手把手教你集成外部应用和drools workbench6.1 1.         首先按照官方文档安装workbench ,我用的是最完整版的jbpm6-console的平台系统,里面既包含j ...

  9. 体验报告:微信小程序在安卓机和苹果机上的区别

    很多人可能会问:微信小程序和在微信里面浏览一个网页有什么区别? 首先,小程序的运行是全屏的,界面跟进入了一个APP很像,更为沉浸跟在微信里面访问h5不一样:其次,它的浏览体验更为稳定. 不过,这还不够 ...

  10. AlloyTouch实战--60行代码搞定QQ看点资料卡

    原文链接:https://github.com/AlloyTeam/AlloyTouch/wiki/kandian 先验货 访问DEMO你也可以点击这里 源代码可以点击这里 如你体验所见,流程的滚动的 ...