EqualityComparer自定义相等比较 电脑版发表于:2021/4/19 15:47 ![.netcore](https://img.tnblog.net/arcimg/hb/c857299a86d84ee7b26d181a31e58234.jpg ".netcore") ># EqualityComparer自定义相等比较 [TOC] <p style="font-weight: 400;line-height: 1.5;color: #212529;-webkit-tap-highlight-color: transparent;box-sizing: border-box;padding: 0px 20px 20px 20px;border: 1px solid #e9ecef;border-left-width: .25rem;border-radius: .25rem;display: block;border-left-color: #5bc0de;"> 自定义实现两个对象的相等比较,一种方案是重写Object类的Equals方法,很easy,如果相等返回true,不相等就返回false。不过,如果把自定义相等的比较用于泛型集,比如Dictionary、HashSet等,这些集合都有一个共同点——必须标识存储项的唯一性,即每一个子项都有对应的key。 object.Equals方法是面向Object类型的,如果用于泛型对象,在判断是否相等的过程需要进行大量的装箱/拆箱操作,尤其是复合类型,由于要进行细致的比较,类型转换更为频繁,这样会带来一定量的性能开销,所以,对于泛型集合的相等比较,应该考虑使用 IEqualityComparer<T>,Dotnet类型提供了一个实现了该接口的抽象类——EqualityComparer<T>。 在实际使用中,不妨直接实现这个抽象类,好处是该抽象类公开了一个静态的Default属性,可以返回平台默认的比较方案。因此,实现该抽象类的好处在于,既可以提供自定义实现,同时也可以保留默认行为。 我们先来解释一下,为什么在泛型集合中需要用到自定义相等比较。看例子,咱们以常用的Dictionary为例,字典的Key我用一个叫Entity的类来标识,该类定义如下。 </p> ```csharp public class Entity { public int ID { get; set; } public string Name { get; set; } } ``` >然后,我们实例化一个字典,并向其中添加两个项。 ```csharp IDictionary<Entity, string> dic = new Dictionary<Entity, string>(); dic.Add(new Entity { ID = 1, Name = "小明" }, "C++"); dic.Add(new Entity { ID = 2, Name = "小王" }, "VB"); ``` >接着,从字典中读出Key为ID = 2 , Name = "小王" 的值。 ```csharp Entity findkey = new Entity { ID = 2, Name = "小王" }; if (dic.ContainsKey(findkey)) { Console.WriteLine(dic[findkey]); } ``` <p style="font-weight: 400;line-height: 1.5;color: #212529;-webkit-tap-highlight-color: transparent;box-sizing: border-box;padding: 0px 20px 20px 20px;border: 1px solid #e9ecef;border-left-width: .25rem;border-radius: .25rem;display: block;border-left-color: #5bc0de;"> 在查找时,先实例化一个Entity,然后给ID和Name属性赋上要查找的值,随后调用字典实例的ContainsKey方法判断一下要查找的key是否存在于字典中,如果存在,就输出该key对应的值。 代码看起来很完美,但一旦运行,你会发现什么都没找到。为啥呢? 因为该字典存储项的key是我们自定义的Entity类,当我们要从中查找时,是另外实例化了一个Entity对象,并赋了对应的属性值去查找的,可是问题就来了,我们实例化的findkey对象,与存入到字典中的Entity不是同一个对象,虽然它们的属性值相等,但它们引用的不是同一个实例,因为被判定为不相等的对象,故找不到对应的Key。 这个时候,EqualityComparer就派上用场了,自定义一个类并从它派生,添加自己的代码实现,不管是不是同一个对象实例,只要属性的值相等,则视为相同的key。 </p> ```csharp public sealed class CustomEqComparer : EqualityComparer<Entity> { public override bool Equals(Entity x, Entity y) { if (x.ID == y.ID && x.Name == y.Name) return true; return false; } public override int GetHashCode(Entity obj) { return obj.ID.GetHashCode(); } } ``` <p style="font-weight: 400;line-height: 1.5;color: #212529;-webkit-tap-highlight-color: transparent;box-sizing: border-box;padding: 0px 20px 20px 20px;border: 1px solid #e9ecef;border-left-width: .25rem;border-radius: .25rem;display: block;border-left-color: #5bc0de;"> 实现Equals方法,如果两个对象相等,返回真,否则返回假。GetHashCode方法返回哈希值,算法不应该过于复杂,避免性能开销,只要能够保证相等的两个对象返回相同的哈希值;不相等的对象返回不同的哈希值,这样就可以了。这里直接以ID属性的值为哈希,所以,每个key的ID值不能重复。 自定义完比较器后,只要在实例化字典实例时传给它的构造函数就可以了。把上面的代码改为: </p> ```csharp CustomEqComparer comp = new CustomEqComparer(); IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp); ``` <p style="font-weight: 400;line-height: 1.5;color: #212529;-webkit-tap-highlight-color: transparent;box-sizing: border-box;padding: 0px 20px 20px 20px;border: 1px solid #e9ecef;border-left-width: .25rem;border-radius: .25rem;display: block;border-left-color: #5bc0de;"> 现在,再次执行前面的例子,ID=2,Name="小王"的key就可以查找出来了。 完整的演示代码如下: </p> ```csharp class Program { static void Main(string[] args) { CustomEqComparer comp = new CustomEqComparer(); IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp); dic.Add(new Entity { ID = 1, Name = "小明" }, "C++"); dic.Add(new Entity { ID = 2, Name = "小王" }, "VB"); Entity findkey = new Entity { ID = 2, Name = "小王" }; if (dic.ContainsKey(findkey)) { Console.WriteLine(dic[findkey]); } Console.Read(); } } public class Entity { public int ID { get; set; } public string Name { get; set; } } public sealed class CustomEqComparer : EqualityComparer<Entity> { public override bool Equals(Entity x, Entity y) { if (x.ID == y.ID && x.Name == y.Name) return true; return false; } public override int GetHashCode(Entity obj) { return obj.ID.GetHashCode(); } } ``` tn>原作者地址:https://www.cnblogs.com/tcjiaan/p/5700192.html