EqualityComparer 是 .NET 中用于比较两个对象是否相等的一个抽象类,它定义了判断“相等”的标准。默认情况下,引用类型通过引用地址判断是否相等,值类型则逐字段比较。但在很多场景下,比如集合操作(如 Dictionary、HashSet、LINQ 的 Distinct、GroupBy 等),我们需要自定义“相等”的逻辑,这时就可以使用或实现 EqualityComparer。
默认的 EqualityComparer
System.Collections.Generic.EqualityComparer是泛型类,为类型 T 提供相等性比较服务。它会自动选择最合适的比较器:
如果 T 实现了 IEquatable
这意味着,只要你的类实现了 IEquatable
例如:
public class Person : IEquatable<Person>
{
public string Name { get; set; }
public int Age { get; set; }
<pre class="brush:php;toolbar:false;">public bool Equals(Person other)
{
if (other is null) return false;
return Name == other.Name && Age == other.Age;
}
public override bool Equals(object obj) => Equals(obj as Person);
public override int GetHashCode() => HashCode.Combine(Name, Age);}
这样在 HashSet
自定义 EqualityComparer 类
如果你不能修改原始类(比如第三方类型),或者想在不同场景下使用不同的比较逻辑,可以继承 EqualityComparer
例如,只按姓名比较 Person:
public class PersonNameComparer : EqualityComparer<Person>
{
public override bool Equals(Person x, Person y)
{
if (x is null && y is null) return true;
if (x is null || y is null) return false;
return x.Name == y.Name;
}
<pre class="brush:php;toolbar:false;">public override int GetHashCode(Person obj)
{
if (obj is null) return 0;
return obj.Name?.GetHashCode() ?? 0;
}}
使用方式:
var people = new List<Person> { /* ... */ };
var distinctPeople = people.Distinct(new PersonNameComparer());
使用静态方法创建轻量比较器
.NET Core 2.1+ 提供了一个便捷方法:EqualityComparer
例如,创建一个通用的委托比较器:
public class DelegateComparer<T> : EqualityComparer<T>
{
private readonly Func<T, T, bool> _equals;
private readonly Func<T, int> _getHashCode;
<pre class="brush:php;toolbar:false;">public DelegateComparer(Func<T, T, bool> equals, Func<T, int> getHashCode)
{
_equals = equals;
_getHashCode = getHashCode;
}
public override bool Equals(T x, T y) => _equals(x, y);
public override int GetHashCode(T obj) => _getHashCode(obj);}
使用示例:
var comparer = new DelegateComparer<Person>(
(p1, p2) => p1?.Name == p2?.Name,
p => p.Name?.GetHashCode() ?? 0
);
关键注意事项
Equals 和 GetHashCode 必须一致:如果两个对象 Equals 返回 true,它们的 GetHashCode 必须返回相同值。 GetHashCode 不应抛出异常,且在对象生命周期内对于参与比较的字段应保持稳定。 避免在 GetHashCode 中使用可变字段,否则可能导致 HashSet 或 Dictionary 出现不可查找的问题。基本上就这些。通过实现 IEquatable
