惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
爱范儿
爱范儿
罗磊的独立博客
美团技术团队
Jina AI
Jina AI
量子位
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
酷 壳 – CoolShell
酷 壳 – CoolShell
有赞技术团队
有赞技术团队
V
V2EX
阮一峰的网络日志
阮一峰的网络日志
小众软件
小众软件
IT之家
IT之家
雷峰网
雷峰网
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
月光博客
月光博客
人人都是产品经理
人人都是产品经理
博客园 - 三生石上(FineUI控件)

博客园 - 彭旭

VNC远程Gnome图形桌面(openEuler 20.03 LTS系统) [WPF]控件应用多个样式 [ASP.NET MVC] Real-time之HTML5 服务器发送事件(server-sent event) [WCF]DomainServices客户端操作异常处理 [Silverlight]监听指定控件(FrameworkElement)的依赖属性(DependencyProperty)的更改 [Silverlight]键盘钩子KeyboardHook [Chrome扩展]屏蔽Google搜索结果重定向 [Winform/C#]点击工作区域移动窗体 [WinForm] C#避免Panel(ScrollableControl)的子控件获取焦点时滚动条自动滚动 C#远程连接Oracle数据库(不安装客户端) RichTextBox.MouseWheel事件控制父控件Panel的内容滚动 ID3v1信息结构(MP3文件)的读取、修改(C#) 在线编辑Office文件 SQL优化 将自然数分解为若干连续的自然数 小写金额转换为大写金额 大数阶乘 查看文件夹大小及占用空间 VS2005开发WAP网站初识 - 彭旭 - 博客园
Linq Distinct扩展
彭旭 · 2012-12-12 · via 博客园 - 彭旭

前言

问题描述:Person类有两个属性ID(int)、Name(string)属性。筛选序列中不重复的Person。

   1: public class Person
   2: {
   3:     public int ID { get; set; }
   4:     public string Name { get; set; }
   5: }

好吧,看样子得用Distinct方法。不过Distinct方法有两个重载。

   1: public static IEnumerable<TSource> Distinct<TSource>(
   2:     this IEnumerable<TSource> source
   3: )
   4:  
   5: public static IEnumerable<TSource> Distinct<TSource>(
   6:     this IEnumerable<TSource> source,
   7:     IEqualityComparer<TSource> comparer
   8: )

看样子已经够用了。不过如果我想只按ID唯一,或者按Name唯一呢?派生IEqualityComparer倒是可以解决。不过那也忒坑爹了。还是自己试着写一个扩展吧。

Distinct扩展

版本1

参考封装的Distinct的方法先写一个。声明一个List<TSource>,循环的时候比较,如果不同就加进去。

   1: public static IEnumerable<TSource> Distinct<TSource, TProperty>(this IEnumerable<TSource> source, Func<TSource, TProperty> selector)
   2: {
   3:     if (source == null) throw new ArgumentNullException("source");
   4:     if (selector == null) throw new ArgumentNullException("selector");
   5:     List<TSource> temp = new List<TSource>();
   6:     foreach (var item in source)
   7:     {
   8:         if (temp.Any(t => selector(t).Equals(selector(item))))
   9:         {
  10:             continue;
  11:         }
  12:         temp.Add(item);
  13:     }
  14:     return temp;
  15: }

测试一下,没什么问题。不过在函数里声明List<TSource>感觉怪怪的,想办法换种方式。

   1: List<Person> list = new List<Person>() 
   2: {
   3:     new Person() { ID = 1, Name = "Alen1" },
   4:     new Person() { ID = 1, Name = "Alen2" },
   5:     new Person() { ID = 2, Name = "Alen2" },
   6:     new Person() { ID = 2, Name = "Alen1" }
   7: };
   8:  
   9: var temp = list.Distinct(t => t.ID);

版本2

取指定属性的唯一序列,在循环的时候使用yield返回。

   1: public static IEnumerable<TSource> Distinct<TSource, TProperty>(this IEnumerable<TSource> source, Func<TSource, TProperty> selector)
   2: {
   3:     if (source == null) throw new ArgumentNullException("source");
   4:     if (selector == null) throw new ArgumentNullException("selector");
   5:  
   6:     var pdis = source.Select(t => selector(t)).Distinct();
   7:     foreach (var item in pdis)
   8:     {
   9:         yield return source.First(t => selector(t).Equals(item));
  10:     }
  11: }

测试通过。

用Stopwatch分别测试两个函数循环不同次数的时间。对比一下,循环次数在10,000次以上时,版本2耗时大概是版本1的1/40,性能差距算是比较大。

后记

目前来说已经够用了,如果还有其他需求,再根据情况扩展吧。

   1: list.Distinct(t => t.ID);
   2: list.Distinct(t => new { t.ID, t.Name }); //根据ID及Name的值分别比较来取唯一,并非比较引用