【.net 深呼吸】EqualityComparer——自定义相等比较
自定义实现两个对象的相等比较,一种方案是重写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——自定义相等比较的更多相关文章
- 【.net 深呼吸】自定义特性(Attribute)的实现与检索方法
在.net的各个语言中,尤其是VB.NET和C#,都有特性这一东东,具体的概念,大家可以网上查,这里老周说一个非标准的概念——特性者,就是对象的附加数据.对象自然可以是类型.类型成员,以及程序集. 说 ...
- 【.net 深呼吸】自定义缓存配置(非Web项目)
在前一篇烂文中,老周简单讲述了非Web应用的缓存技术的基本用法.其实嘛,使用系统默认方案已经满足我们的需求了,不过,如果你真想自己来配置缓存,也是可以的. 缓存的自定义配置可以有两种方案,一种是用代码 ...
- 【.net 深呼吸】自定义应用程序配置节
实际上,应用程序配置文件 App.config,是由各个节(Configuration Section)组成的,通常,配置节是按功能划分的,比如我们很熟悉的 appSettings.connectio ...
- 【.NET深呼吸】基础:自定义类型转换
照例,老周在开始吹牛之前,先讲讲小故事,这是朋友提出的建议,老TMD写技术有什么了不起的,人人都会写.后来老周想想,也确实,代码谁不会写,能写到有品位有感悟,就不容易做到.于是,老周接受了该朋友的建议 ...
- C#集合 -- 自定义集合与代理
前面章节所讨论的集合都可以直接实例化,因此我们可以非常方便地使用这些集合类.但是如果你试图在集合添加或移除元素时添加控制,它们就不适用了.对于强类型集合,在某些情况下,你需要添加这样的控制: 添加或移 ...
- [C#] 类型学习笔记三:自定义值类型
既前两篇之后,这一篇我们讨论通过struct 关键字自定义值类型. 在第一篇已经讨论过值类型的优势,节省空间,不会触发Gargage Collection等等. 在对性能要求比较高的场景下,通过str ...
- 自定义值类型一定不要忘了重写Equals,否则性能和空间双双堪忧
一:背景 1. 讲故事 曾今在项目中发现有同事自定义结构体的时候,居然没有重写Equals方法,比如下面这段代码: static void Main(string[] args) { var list ...
- 栈(Stack) --- C# 自定义和微软官方的区别
最近在学习算法基础,本篇文章作为一个记录,也算是一次实践和总结.(顺便也深入C#运行时学习一下) 目录 1. 栈是什么 2. Stack 自定义实现 3. Stack C#官方实现 4. 区别 5. ...
- 【.net 深呼吸】细说CodeDom(7):索引器
在开始正题之前,先补充一点前面的内容. 在方法中,如果要引用方法参数,前面的示例中,老周使用的是 CodeVariableReferenceExpression 类,它用于引用变量,也适用于引用方法参 ...
随机推荐
- Java多线程基础学习(一)
1. 创建线程 1.1 通过构造函数:public Thread(Runnable target, String name){} 或:public Thread(Runnable target ...
- ASP.NET Aries 入门开发教程5:自定义列表页工具栏区
前言: 抓紧时间,继续写教程,因为发现用户期待的内容,都在业务处理那一块. 不得不继续勤劳了. 这节主要介绍工具栏区的玩法. 工具栏的默认介绍: 工具栏默认包括5个按钮,根据不同的权限决定显示: 添加 ...
- 自定义Inspector检视面板
Unity中的Inspector面板可以显示的属性包括以下两类:(1)C#以及Unity提供的基础类型:(2)自定义类型,并使用[System.Serializable]关键字序列化,比如: [Sys ...
- C# 正则表达式大全
文章导读 正则表达式的本质是使用一系列特殊字符模式,来表示某一类字符串.正则表达式无疑是处理文本最有力的工具,而.NET提供的Regex类实现了验证正则表达式的方法.Regex 类表示不可变(只读)的 ...
- js获取给定月份的N个月后的日期
1.在讲js获取给定月份的N个月后的日期之前,小颖先给大家讲下getFullYear().getYear()的区别. ①getYear() var d = new Date() console.log ...
- inline-block元素间距问题的几种解决方案
不知道大家有没有碰到过设置了display:inline-block;的几个相邻元素之间有几px间距的问题,这里提供几种简单实用的解决方法,希望能够帮到大家! 方法1. 将<li>标签 ...
- Asp.Net WebApi核心对象解析(上篇)
生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...
- 程序员必须要知道的Hadoop的一些事实
程序员必须要知道的Hadoop的一些事实.现如今,Apache Hadoop已经无人不知无人不晓.当年雅虎搜索工程师Doug Cutting开发出这个用以创建分布式计算机环境的开源软...... 1: ...
- DOM的小练习,两个表格之间数据的移动
本次讲的是两个表格之间数据的移动,左边的表格移动到右边,并且左边表格移动内容消失. <head> <meta http-equiv="Content-Type" ...
- Cesium简介以及离线部署运行
Cesium简介 cesium是国外一个基于JavaScript编写的使用WebGL的地图引擎,一款开源3DGIS的js库.cesium支持3D,2D,2.5D形式的地图展示,可以自行绘制图形,高亮区 ...