博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
何止 Linq 的 Distinct 不给力(转)
阅读量:5822 次
发布时间:2019-06-18

本文共 6973 字,大约阅读时间需要 23 分钟。

昨日看到一篇文章 《》,文中指出 Linq 中 Distinct 方法的一个重载使用了 IEqualityComparer<T> 作为参数,调用时大多都要创建新的类去实现这个接口,很不给力。文中给出了一种解决办法,略显烦索,我也写了《》一文使用予以简化。

但问题远远没有结束,不给力是因为使用了 IEqualityComparer<T> 作为参数,而 .net 中将 IEqualityComparer<T> 用作参数的地方相当多:

IEqualityComparer<T> 用作参数

.net 中 IEqualityComparer<T> 用作参数,大致可分为以下两种情况:

1. Linq

1234567891011121314151617181920
public static class Enumerable{    public static bool Contains
(this IEnumerable
source, TSource value, comparer); public static IEnumerable
Distinct
(this IEnumerable
source, comparer); public static IEnumerable
Except
(this IEnumerable
first, IEnumerable
second, comparer); public static IEnumerable
> GroupBy
(this IEnumerable
source, Func
keySelector, comparer); public static IEnumerable
Intersect
(this IEnumerable
first, IEnumerable
second, comparer); public static bool SequenceEqual
(this IEnumerable
first, IEnumerable
second, comparer); public static Dictionary
ToDictionary
(this IEnumerable
source, Func
keySelector, ); public static ILookup
ToLookup
(this IEnumerable
source, Func
keySelector, comparer); public static IEnumerable
Union
(this IEnumerable
first, IEnumerable
second, comparer); //...}

同样 Queryable 类中也有类似的一些方法

2. 字典、集合类

123456789101112131415161718
1920
public class Dictionary
: IDictionary
, ICollection
>,
IEnumerable
>, IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback{ public Dictionary(); public Dictionary(IDictionary
dictionary); public Dictionary( comparer); public Dictionary(int capacity); public Dictionary(IDictionary
dictionary, comparer); public Dictionary(int capacity, comparer); //...}public class HashSet
: ISerializable, IDeserializationCallback, ISet
, ICollection
, IEnumerable
, IEnumerable{ public HashSet(); public HashSet(IEnumerable
collection); public HashSet( comparer); public HashSet(IEnumerable
collection, comparer); //...}

Dictionary<TKey, TValue> 和 HashSet<T> 类的构造函数都用到了 IEqualityComparer<T> 接口。

除了如上两个,还有 ConcurrentDictionary<TKey, TValue>、SortedSet<T>、KeyedCollection<TKey, TItem>(抽象类)、SynchronizedKeyedCollection<K, T> 等等也使用 IEqualityComparer<T> 接口作为构造函数的参数。

 

IEqualityComparer<T> 作为参数多在复杂的重载中出现,满足一些特殊情况的要求,而相应的简单的重载确是经常使用的。因此,虽然 IEqualityComparer<T> 在 .net 应用广泛,但在我们编程时,确是较少涉及。

不过话又说回来,一旦使用到时,就会感觉相当麻烦。多数时候你不得不去创建一个新类,去实现 IEqualityComparer<T> 接口,再去 new 一个实例,而你真正需要的可能仅仅是根据某个属性(如 ID )进行比较。创建新类实现 IEqualityComparer<T> 接口,不但增加了代码量,还增加的复杂度:你要考虑这个新类放在哪里合适,如何命名等等。

因此,我们期望有一个简单的方法来能直接创建 IEqualityComparer<T> 的实例。《》一文中给出了一个简单实用的类 CommonEqualityComparer<T, V>,在这里可以复用来达到我们的目标。

CommonEqualityComparer<T, V>

12345678910111213141516171819202122232425262728
using System;using System.Collections.Generic;using System.Runtime.CompilerServices;using System.Linq;public class CommonEqualityComparer
: IEqualityComparer
{ private Func
keySelector; private IEqualityComparer
comparer; public CommonEqualityComparer(Func
keySelector, IEqualityComparer
comparer) { this.keySelector = keySelector; this.comparer = comparer; } public CommonEqualityComparer(Func
keySelector) : this(keySelector, EqualityComparer
.Default) { } public bool Equals(T x, T y) { return comparer.Equals(keySelector(x), keySelector(y)); } public int GetHashCode(T obj) { return comparer.GetHashCode(keySelector(obj)); }}

使用这个类,可以简易通过 lambda 表达式来创建 IEqualityComparer<T> 的实例:

123456
var dict = new Dictionary
();List
persons = null;Person p1 = null;//...var ps = persons.Contains(p1, );

相信看了上面代码的,你会觉得 new CommonEqualityComparer<Person, string>(p => p.Name)) 太冗长。不过我们可以借助下面的类加以改善:

1234567891011
public static class Equality
{ public static IEqualityComparer
CreateComparer
(Func
keySelector) { return new CommonEqualityComparer
(keySelector); } public static IEqualityComparer
CreateComparer
(Func
keySelector, IEqualityComparer
comparer) { return new CommonEqualityComparer
(keySelector, comparer); }}

调用代码可简化:

12
var dict = new Dictionary
();var ps = persons.Contains(p1, );

不考虑类名和方法名的前提下,Equality<Person>.CreateComparer(p => p.ID) 的写法也经精简到极限了(如果你能进一步精简,不妨告诉我)

其实有了 Equality<T> 这个类,我们大可将 CommonEqualityComparer<T, V> 类封装隐藏起来。

Equality<T> 类

1234567891011121314151617181920212223242526272829303132333435
public static class Equality
{ public static IEqualityComparer
CreateComparer
(Func
keySelector) { return new CommonEqualityComparer
(keySelector); } public static IEqualityComparer
CreateComparer
(Func
keySelector, IEqualityComparer
comparer) { return new CommonEqualityComparer
(keySelector, comparer); } class CommonEqualityComparer
: IEqualityComparer
{ private Func
keySelector; private IEqualityComparer
comparer; public CommonEqualityComparer(Func
keySelector, IEqualityComparer
comparer) { this.keySelector = keySelector; this.comparer = comparer; } public CommonEqualityComparer(Func
keySelector) : this(keySelector, EqualityComparer
.Default) { } public bool Equals(T x, T y) { return comparer.Equals(keySelector(x), keySelector(y)); } public int GetHashCode(T obj) { return comparer.GetHashCode(keySelector(obj)); } }}

CommonEqualityComparer<T, V> 封装成了 Equaility<T> 的嵌套类 CommonEqualityComparer<V>,对外不可见,降低了使用的复杂度。

《》一文中的 写起来也简单了:

1234567891011
public static class DistinctExtensions{    public static IEnumerable
Distinct
(this IEnumerable
source, Func
keySelector) { return source.Distinct(); } public static IEnumerable
Distinct
(this IEnumerable
source, Func
keySelector, IEqualityComparer
comparer) { return source.Distinct); }}

Linq 中除 Distinct 外还有众多方法使用了 IEqualityComparer<T> 接口,逐一扩展未必是一个好方式,使用 Equality<T>.CreateComparer 方法比较明智。

总结

.net 中经常把 IEqualityComparer<T> 用作某些重载的参数。

虽然这些重载在日常使用中并不频繁,不过一旦用到,大多要创建新类实现 IEqualityComparer<T>,繁琐不给力。
本文创建 Equality<T> 泛型类,配合一个 lambda 表达式可快速创建 IEqualityComparer<T> 的实例。

转载于:https://www.cnblogs.com/Yjianyong/archive/2011/12/28/2304409.html

你可能感兴趣的文章
爱加密邀您参加5月17日深圳App开发沙龙活动
查看>>
Python 数据库到处到Excel
查看>>
R语言 并行计算parallel包
查看>>
HTTP状态码笔记
查看>>
h5 FileReader 上传文件
查看>>
分布式项目(六)iot-device-data 设备数据监控
查看>>
RHCE 学习笔记(39) - LDAP 服务器,NFS和autofs
查看>>
Java多线程的生产者、消费者实现例子
查看>>
Kafka常用操作
查看>>
ubuntu下允许root用户ssh远程登录
查看>>
java代码构建一棵二叉树(二叉查找树)
查看>>
salt认证和语法
查看>>
WGS84,GCJ02, BD09坐标转换
查看>>
页面开发问题集
查看>>
部署SCCM 2012R2之六:配置发现篇
查看>>
配置托管服务帐号
查看>>
我的友情链接
查看>>
cacti故障记录
查看>>
***常用兵器之扫描篇(下)
查看>>
maven异常:missing artifact jdk.tools:jar:1.6
查看>>