C#中按单个字段去重:如何去除对象重复项

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

问题:Distinct()无法按字段过滤

\n\n

想象一下:你有一个客户列表,其中混入了按邮箱地址重复的项。你写下:

\n\n
var uniqueClients = clients.Distinct().ToList();
\n\n

然后惊讶地发现重复项并没有消失。为什么?因为标准的Distinct()是按整个对象的引用(或者如果你重写了Equals,则按Equals)进行比较。它不知道对你来说,“唯一性”指的是唯一的Email,而不是唯一的类实例。

\n\n

这个问题是Stack Overflow上关于LINQ最热门的问题之一(超过50万次浏览)。今天我们将介绍在C#中解决这个问题的3种实用方法。开始吧!

\n\n

方法1:GroupBy + First(经典方法)

\n\n

最简单易懂的方法,无需安装第三方库。思路:按所需属性对对象进行分组,然后从每个分组中取出第一个元素。

\n\n
var uniqueByEmail = clients\n    .GroupBy(c => c.Email)\n    .Select(g => g.First())\n    .ToList();
\n\n

工作原理:

\n
    \n
  • GroupBy(c => c.Email) — 将集合按邮箱地址作为键进行分组。
  • \n
  • Select(g => g.First()) — 从每个分组中取出第一个对象。
  • \n
  • 其余重复项被丢弃。
  • \n
\n\n

优点:

\n
    \n
  • 开箱即用(只需using System.Linq;)。
  • \n
  • 即使对于新手来说也直观易懂。
  • \n
  • 保留原始元素顺序(第一个遇到的对象被保留)。
  • \n
\n\n

缺点:

\n
    \n
  • 在处理大数据时稍慢(需要创建分组)。
  • \n
  • 代码比期望的略显冗长。
  • \n
\n\n

方法2:MoreLinq和DistinctBy(优雅之选)

\n\n

如果你不介意使用第三方库,MoreLinq提供了完美的DistinctBy方法。它完全按照你对Distinct的期望工作——按属性过滤。

\n\n

通过NuGet安装:

\n
Install-Package MoreLinq
\n\n

使用方法:

\n
using MoreLinq;\n\nvar uniqueByEmail = clients.DistinctBy(c => c.Email).ToList();
\n\n

一行代码,搞定一切。内部DistinctBy使用HashSet,时间复杂度为O(n)。

\n\n

优点:

\n
    \n
  • 代码极其简洁且可读性强。
  • \n
  • 高性能。
  • \n
  • 该库经过充分测试(下载量超过1000万次)。
  • \n
\n\n

缺点:

\n
    \n
  • 需要在项目中添加依赖。
  • \n
  • 并非所有团队都允许使用第三方库。
  • \n
\n\n

方法3:自定义IEqualityComparer(最大控制权)

\n\n

当你不想引入外部库,但又希望在多个地方复用比较逻辑时,此方法适用。创建你自己的比较器:

\n\n
public class ClientEmailComparer : IEqualityComparer<Client>\n{\n    public bool Equals(Client x, Client y)\n    {\n        if (ReferenceEquals(x, y)) return true;\n        if (x is null || y is null) return false;\n        return x.Email == y.Email;\n    }\n\n    public int GetHashCode(Client obj)\n    {\n        return obj.Email?.GetHashCode() ?? 0;\n    }\n}
\n\n

使用方法:

\n
var comparer = new ClientEmailComparer();\nvar uniqueByEmail = clients.Distinct(comparer).ToList();
\n\n

优点:

\n
    \n
  • 完全控制比较逻辑。
  • \n
  • 比较器可在Contains、Except、Union等方法中复用。
  • \n
  • 无需外部库。
  • \n
\n\n

缺点:

\n
    \n
  • 样板代码较多。
  • \n

博客

书籍推荐