1. 程式人生 > >C#中Equals和GetHashCode

C#中Equals和GetHashCode

Equals和GetHashCode

Equals每個實現都必須遵循以下約定:

  • 自反性(Reflexive): x.equals(x)必須返回true.
  • 對稱性(Symmetric): x.equals(y)為true時,y.equals(x)也為true.
  • 傳遞性(Transitive): 對於任何非null的應用值x,y和z,如果x.equals(y)返回true,並且y.equals(z)也返回true,那麼x.equals(z)必須返回true.
  • 一致性(Consistence): 如果多次將物件與另一個物件比較,結果始終相同.只要未修改x和y的應用物件,x.equals(y)連續呼叫x.equals(y)返回相同的值l.
  • 非null(Non-null): 如果x不是null,y為null,則x.equals(y)必須為false

GetHashCode:

  • 兩個相等物件根據equals方法比較時相等,那麼這兩個物件中任意一個物件的hashcode方法都必須產生同樣的整數。
  • 在我們未對物件進行修改時,多次呼叫hashcode使用返回同一個整數.在同一個應用程式中多次執行,每次執行返回的整數可以不一致.
  • 如果兩個物件根據equals方法比較不相等時,那麼呼叫這兩個物件中任意一個物件的hashcode方法,不一同的整數。但不同的物件,產生不同整數,有可能提高散列表的效能.

IEqualityComparer實現

下面我們建立一個學生類,從而進一步的實現我們物件資料的對比

    public class Student
    {
        public string Name { get; set; }

        public int Age { get; set; }
    }

通過如下程式碼我們將通過distinct方法實現我們的過濾.

    class Program
    {
        static void Main(string[] args)
        {
            List<Student> students = new List<Student>
            {
                new Student{ Name = "MR.A", Age = 32},
                new Student{ Name = "MR.B", Age = 34},
                new Student{ Name = "MR.A", Age = 32}  
            };
            Console.WriteLine("distinctStudents has Count = {0}", students.Distinct().Count());//distinctStudents has Count = 3
            Console.ReadLine();
        }
    }

我們需要達到的是忽略相同資料的物件,但是並沒有達到我們如期的效果.因為是distinct預設比較的是物件的引用...所以這樣達不到我們預期效果.那我們修改一下來實現我們預期效果.

在預設情況下Equals具有以下行為:

  • 如果例項是引用型別,則只有引用相同時, Equals才會返回true。
  • 如果例項是值型別,則僅當型別和值相同時, Equals才會返回true。

Distinct(IEnumerable, IEqualityComparer)

通過使用指定的 IEqualityComparer 對值進行比較,返回序列中的非重複元素.

型別引數

  • TSource source 的元素型別。

    引數

  • source IEnumerable 要從中移除重複元素的序列。
  • comparer IEqualityComparer 用於比較值的 IEqualityComparer

返回

  • IEnumerable
    一個包含源序列中的非重複元素的 IEnumerable

我們來看如下程式碼片段

    public class StudentComparator : EqualityComparer<Student>
    {
        public override bool Equals(Student x,Student y)
        {
            return x.Name == y.Name && x.Age == y.Age;
        }

        public override int GetHashCode(Student obj)
        {
            return obj.Name.GetHashCode() * obj.Age;
        }
    }

上述程式碼片段如果兩個Equals返回的true並且GetHashCode返回相同的雜湊碼,則認為兩個物件相等.

重寫Equals和GetHashCode

var stu1 = new Student { Name = "MR.A", Age = 32 };
var stu2 = new Student { Name = "MR.A", Age = 32 };
   
bool result = stu1.Equals(stu2); //false because it's reference Equals

上述程式碼片段執行後結果非預期效果.我們將進一步的去實現程式碼,以達到預期效果....

    public class Student
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public override bool Equals(object obj)
        {
            var stu = obj as Student;
            if (stu == null) return false;
            return Name == stu.Name && Age == stu.Age; 
        }
        public override int GetHashCode()
        {
            return Name.GetHashCode() * Age;
        }
    }
    
  var stu1 = new Student { Name = "MR.A", Age = 32 };
  var stu2 = new Student { Name = "MR.A", Age = 32 };

  bool result = stu1.Equals(stu2); //result is true

我們再使用LINQ Distinct方法進行過濾和查詢,同時將會檢查Equals和GetHashCode

    List<Student> students = new List<Student>
    {
        new Student{ Name = "MR.A", Age = 32},
        new Student{ Name = "MR.B", Age = 34},
        new Student{ Name = "MR.A", Age = 32}
    };
    Console.WriteLine("distinctStudents has Count = {0}", students.Distinct().Count()); //distinctStudents has Count = 2