【.net 深呼吸】EqualityComparer——自定義相等比較


自定義實現兩個對象的相等比較,一種方案是重寫Object類的Equals方法,很easy,如果相等返回true,不相等就返回false。不過,如果把自定義相等的比較用於泛型集,比如Dictionary、HashSet等,這些集合都有一個共同點——必須標識存儲項的唯一性,即每一個子項都有對應的key。

object.Equals方法是面向Object類型的,如果用於泛型對象,在判斷是否相等的過程需要進行大量的裝箱/拆箱操作,尤其是復合類型,由於要進行細致的比較,類型轉換更為頻繁,這樣會帶來一定量的性能開銷,所以,對於泛型集合的相等比較,應該考慮使用 IEqualityComparer<T>,Dotnet類型提供了一個實現了該接口的抽象類——EqualityComparer<T>。

在實際使用中,不妨直接實現這個抽象類,好處是該抽象類公開了一個靜態的Default屬性,可以返回平台默認的比較方案。因此,實現該抽象類的好處在於,既可以提供自定義實現,同時也可以保留默認行為。

 

我們先來解釋一下,為什么在泛型集合中需要用到自定義相等比較。看例子,咱們以常用的Dictionary為例,字典的Key我用一個叫Entity的類來標識,該類定義如下。

    public class Entity
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

 

然后,我們實例化一個字典,並向其中添加兩個項。

            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 = "小王" 的值。

            Entity findkey = new Entity
            {
                ID = 2,
                Name = "小王"
            };

            if (dic.ContainsKey(findkey))
            {
                Console.WriteLine(dic[findkey]);
            }

在查找時,先實例化一個Entity,然后給ID和Name屬性賦上要查找的值,隨后調用字典實例的ContainsKey方法判斷一下要查找的key是否存在於字典中,如果存在,就輸出該key對應的值。

 

代碼看起來很完美,但一旦運行,你會發現什么都沒找到。為啥呢?

因為該字典存儲項的key是我們自定義的Entity類,當我們要從中查找時,是另外實例化了一個Entity對象,並賦了對應的屬性值去查找的,可是問題就來了,我們實例化的findkey對象,與存入到字典中的Entity不是同一個對象,雖然它們的屬性值相等,但它們引用的不是同一個實例,因為被判定為不相等的對象,故找不到對應的Key。

 

這個時候,EqualityComparer就派上用場了,自定義一個類並從它派生,添加自己的代碼實現,不管是不是同一個對象實例,只要屬性的值相等,則視為相同的key。

    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();
        }
    }

實現Equals方法,如果兩個對象相等,返回真,否則返回假。GetHashCode方法返回哈希值,算法不應該過於復雜,避免性能開銷,只要能夠保證相等的兩個對象返回相同的哈希值;不相等的對象返回不同的哈希值,這樣就可以了。這里直接以ID屬性的值為哈希,所以,每個key的ID值不能重復。

 

自定義完比較器后,只要在實例化字典實例時傳給它的構造函數就可以了。把上面的代碼改為:

            CustomEqComparer comp = new CustomEqComparer();
            IDictionary<Entity, string> dic = new Dictionary<Entity, string>(comp);

 

現在,再次執行前面的例子,ID=2,Name="小王"的key就可以查找出來了。

完整的演示代碼如下:

    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();
        }
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM