c# 扩展方法奇思妙用基础篇八:Distinct 扩展
刚看了篇文章 《Linq的Distinct太不给力了》,文中给出了一个解决办法,略显复杂。
试想如果能写成下面的样子,是不是更简单优雅
var p1 = products.Distinct(p => p.ID);
var p2 = products.Distinct(p => p.Name);
使用一个简单的 lambda 作为参数,也符合 Linq 一贯的风格。
可通过扩展方法实现:
Distinct 扩展方法
首先,创建一个通用比较的类,实现 IEqualityComparer<T> 接口:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Linq; public class CommonEqualityComparer<T, V> : IEqualityComparer<T>
{
private Func<T, V> keySelector; public CommonEqualityComparer(Func<T, V> keySelector)
{
this.keySelector = keySelector;
} public bool Equals(T x, T y)
{
return EqualityComparer<V>.Default.Equals(keySelector(x), keySelector(y));
} public int GetHashCode(T obj)
{
return EqualityComparer<V>.Default.GetHashCode(keySelector(obj));
}
}
第 17 行,用到了 EqualityComparer<T> 类,本文最后有简要说明。
借助上面这个类,Distinct 扩展方法就很好写了:
public static class DistinctExtensions
{
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
}
}
呵呵,简单吧!
Distinct 使用示例
根据 ID :
var data1 = new Person[] {
new Person{ ID = , Name = "鹤冲天"},
new Person{ ID = , Name = "ldp"}
};
var ps1 = data1
.Distinct(p => p.ID)
.ToArray();
根据 Name:
var data2 = new Person[] {
new Person{ ID = , Name = "鹤冲天"},
new Person{ ID = , Name = "鹤冲天"}
};
var ps2 = data2
.Distinct(p => p.Name)
.ToArray();
看了回复后,我做了些改进,推荐使用下面的方式
改进
回复中有朋友提到“不区分大小写地排除重复的字符串”,也不难实现,只需要把上面的代码改进下就 OK:
CommonEqualityComparer<T, V> 类:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Linq; public class CommonEqualityComparer<T, V> : IEqualityComparer<T>
{
private Func<T, V> keySelector;
private IEqualityComparer<V> comparer; public CommonEqualityComparer(Func<T, V> keySelector, IEqualityComparer<V> comparer)
{
this.keySelector = keySelector;
this.comparer = comparer;
} public CommonEqualityComparer(Func<T, V> keySelector)
: this(keySelector, EqualityComparer<V>.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));
}
}
Distinct 扩展方法:
public static class DistinctExtensions
{
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector));
} public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector, IEqualityComparer<V> comparer)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector, comparer));
}
}
借助可选参数,这两个扩展方法也可以合成一个:
public static IEnumerable<T> Distinct<T, V>(this IEnumerable<T> source, Func<T, V> keySelector,
IEqualityComparer<V> comparer = EqualityComparer<V>.Default)
{
return source.Distinct(new CommonEqualityComparer<T, V>(keySelector, comparer));
}
(同样,CommonEqualityComparer<T, V>类的两个构造函数也可以合二为一)
使用示例:
var data3 = new Person[] {
new Person{ ID = , Name = "LDP"},
new Person{ ID = , Name = "ldp"}
};
var ps3 = data3
.Distinct(p => p.Name, StringComparer.CurrentCultureIgnoreCase)
.ToArray();
EqualityComparer<T> 类 简要说明
EqualityComparer<T>为 IEqualityComparer<T> 泛型接口的实现提供基类,它在 .net 4 中有五个重要的子类,见下图:
这五个子类分别用不同类型数据的相等性比较,从类名我们可以略知一二。
这五个子类都是内部类(internal),不能直接访问,EqualityComparer<T> 类提供一个简单的属性 Default。EqualityComparer<T> 会根据传入的 T 的类型,加载不同的子类,并会予以缓存提高性能。
-------------------
思想火花,照亮世界
c# 扩展方法奇思妙用基础篇八:Distinct 扩展的更多相关文章
- c# 扩展方法奇思妙用基础篇八:Distinct 扩展(转载)
转载地址:http://www.cnblogs.com/ldp615/archive/2011/08/01/distinct-entension.html 刚看了篇文章 <Linq的Distin ...
- c# 扩展方法奇思妙用基础篇五:Dictionary<TKey, TValue> 扩展
Dictionary<TKey, TValue>类是常用的一个基础类,但用起来有时确不是很方便.本文逐一讨论,并使用扩展方法解决. 向字典中添加键和值 添加键和值使用 Add 方法,但很多 ...
- c# 扩展方法奇思妙用基础篇九:Expression 扩展
http://www.cnblogs.com/ldp615/archive/2011/09/15/expression-extension-methods.html .net 中创建 Expressi ...
- C# 扩展方法奇思妙用高级篇六:WinForm 控件选择器
在Web开发中,jQuery提供了功能异常强大的$选择器来帮助我们获取页面上的对象.但在WinForm中,.Net似乎没有这样一个使用起来比较方便的选择器.好在我们有扩展方法,可以很方便的打造一个. ...
- c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)
下面是 Queryable 类 中最常用的两个排序的扩展方法: 1 2 public static IOrderedQueryable<TSource> OrderBy<TSourc ...
- c# 扩展方法奇思妙用
# 扩展方法出来已久,介绍扩展方法的文章也很多,但都是笼统的.本人最近一直在思考扩展方法的应用,也悟出了一些,准备将这最近一段时间对扩展方法的思考,写成一个系列文章.每个文章只介绍一个应用方面,篇幅不 ...
- c# 扩展方法奇思妙用集锦
本文转载:http://www.cnblogs.com/ldp615/archive/2009/08/07/1541404.html 其中本人觉得很经典的:c# 扩展方法奇思妙用基础篇五:Dictio ...
- 【mongoDB基础篇②】PHP-mongo扩展的编译以及使用
安装PHP-mongo扩展 安装php-mongo扩展和安装其他php扩展的步骤一样: #1.首先上http://pecl.php.net上面搜索mongo,得到下载地址 wget http://pe ...
- Python基础篇(八)
key words:私有变量,类静态变量,生成器,导入Python模块,r查看模块可以使用的函数,查看帮助信息,启动外部程序,集合,堆,时间模块,random模块,shelve模块,文件读取等 > ...
随机推荐
- fedora25 采用二进制包安装mysql5.5.49
#添加用户和组 groupadd mysql useradd -s /sbin/nologin -g mysql -M mysql /etc/passwd id mysql #安装依赖包 [root@ ...
- Linux C 面试题总结
1.进程和线程的区别,及优劣性比较 进程和线程的主要差别在于它们是不同的操作系统资源管理方式.进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路 ...
- D3.js系列——布局:饼状图和力导向图
一.饼状图 在布局的应用中,最简单的就是饼状图. 1.数据 有如下数据,需要可视化: , , , , ]; 这样的值是不能直接绘图的.例如绘制饼状图的一个部分,需要知道一段弧的起始角度和终止角度,这些 ...
- (转)Vue.use源码分析
我想有过vue开发经验的,对于vue.use并不陌生.当使用vue-resource或vue-router等全局组件时,必须通过Vue.use方法引入,才起作用.那么vue.use在组件引入之前到底做 ...
- unity Chan!下载
http://unity-chan.com/download/datadownload.html
- undefined reference to `std::cout'等错误
(1)gcc和g++都是GNU(组织)的一个编译器. (2)后缀名为.c的程序和.cpp的程序g++都会当成是c++的源程序来处理.而gcc不然,gcc会把.c的程序处理成c程序. (3)对于.cpp ...
- android 完美的tabhost 切换多activity布局
TabHost在很多应用都会使用到,有时候在TabHost添加的Tab中设置view不能满足需求,因为在view中添加如PreferenceActivity相当困难. 之前在一个应用中需要实现使用Ta ...
- ASP.NET MVC传递Model到视图的多种方式总结
ASP.NET MVC传递Model到视图的多种方式总结 有多种方式可以将数据传递到视图,如下所示: ViewData ViewBag PartialView TempData ViewModel T ...
- 将 ASP.NET Core 2.0 项目升级至 ASP.NET Core 2.1
主要升级步骤如下: 将 .csproj 项目文件中的 target framework 改为 netcoreapp2.1 <TargetFramework>netcoreapp2.1< ...
- arm linux c++11编译
include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11 ...