## 快速创建 IEqualityComparer 实例：改进

2013-12-31 23:59 by 鹤冲天, ... 阅读, ... 评论, 收藏, 编辑

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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) { // 此处未处理参数 x 和 y 为空的情况 return comparer.Equals(keySelector(x), keySelector(y)); } public int GetHashCode(T obj) { // 此处未处理参数 obj 为空的情况 return comparer.GetHashCode(keySelector(obj)); } } }

# 关于 null 的两个问题

 1 2 3 4 public class Peron { public string Name { get; set; } }

## 问题一，两个 null 值是否相等？

 1 2 3 4 5 6 7 8 Peron p1 = new Peron { Name = null }; Peron p2 = new Peron { Name = null }; Peron p3 = null; Peron p4 = null; bool b1 = p1.Name == p2.Name; bool b2 = p3 == p4;

## 问题二，为 null 时 HashCode 应该是什么？

 1 2 var h1 = StringComparer.InvariantCulture.GetHashCode(p1.Name); var h2 = EqualityComparer.Default.GetHashCode(p3);

# Equality<T> 改进后的代码

 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37  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) { if (x == null || y == null) return false; return comparer.Equals(keySelector(x), keySelector(y)); } public int GetHashCode(T obj) { if (obj == null) return 0; return comparer.GetHashCode(keySelector(obj)); } } }

 1 2 3 4 5 6 7 8 9 var personNameComparer = Equality.CreateComparer(p => p.Name); // Peron p5 = new Peron { Name = "Bob" }; Peron p6 = new Peron { Name = "Tom" }; var b3 = personNameComparer.Equals(p5, p6); // false // Peron p7 = null; Peron p8 = null; var b4 = personNameComparer.Equals(p7, p8); // false

### 第 28 行代码

 1 2 3 4 5 6 7 8 9 10 if(x== null) { if (y == null) return true; else return false; } else { if (y == null) return false; else return comparer.Equals(keySelector(x), keySelector(y)); }

### 第 33 行代码

 1 return RuntimeHelpers.GetHashCode(null)；

RuntimeHelpers 类在 System.Runtime.CompilerServices 命名空间下，我在反编译 Object 时，在 GetHashCode() 方法中发现了它。

# 复杂情况下的使用

 1 2 3 4 5 6 7 8 public class Employee { public School School { get; set; } } public class School { public string City { get; set; } }

 1 var employeeComparer = Equality.CreateComparer(i => i.School.City);

 1 2 var companylComparer = Equality.CreateComparer(i => i.City); var employeeComparer = Equality.CreateComparer(i => i.School, companylComparer);

 1 2 3 4 5 6 7 8 9 10 var v0 = new Employee { School = new School { City = "Beijing" } }; var v1 = new Employee { School = new School { City = "Beijing" } }; var v2 = new Employee { School = new School { City = "Shanghai" } }; var v3 = new Employee { School = null }; var v4 = new Employee { School = null }; var b1 = employeeComparer.Equals(v0, v1); // true var b2 = employeeComparer.Equals(v0, v2); // false var b3 = employeeComparer.Equals(v0, v3); // false var b4 = employeeComparer.Equals(v3, v4); // false

# 再搞复杂一点

 1 2 3 4 5 6 7 8 9 10 11 12 13 public class Employee { public School School { get; set; } } public class School { public City City { get; set; } } public class City { public string Name { get; set; } public string Country { get; set; } }