試煉25 - 自訂 class 技巧1 怎麼算相等

2022 鐵人賽文 搬回點部落

開始試煉

自訂class 如何算等於
首先 先來自訂class

class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}
void Main()
{
    var a = new Employee { Id = 1, Name = "a" };
    var b = new Employee { Id = 1, Name = "a" };
    a.Equals(b).Dump("a.Equals(b)");
    (a == b).Dump("a == b");

    var c = new List<Employee> { a, b };
    var d = c.Distinct().Dump();
}


可以看到不管 怎樣的等於 跟 用LINQ Distinct
Employee class 都無法算等於

開始 VS2022 來幫忙吧
先點快速動作與重構
選產生Equals 與 GetHashCode…
 


程式碼就產好了

class Employee : IEquatable<Employee>
{
    public int Id { get; set; }
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        return Equals(obj as Employee);
    }

    public bool Equals(Employee other)
    {
        return !(other is null) &&
               Id == other.Id &&
               Name == other.Name;
    }

    public override int GetHashCode()
    {
        int hashCode = -1919740922;
        hashCode = hashCode * -1521134295 + Id.GetHashCode();
        hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
        return hashCode;
    }

    public static bool operator ==(Employee left, Employee right)
    {
        return EqualityComparer<Employee>.Default.Equals(left, right);
    }

    public static bool operator !=(Employee left, Employee right)
    {
        return !(left == right);
    }
}


整理重點
實做 IEquatable<T> 這樣就不用原生的
public override bool Equals(object obj)
減少裝箱拆箱的問題

實做 operator 的 == 跟 !=
讓 自訂物件也可以用 Equals的邏輯做等於比對

GetHashCode 會用到的地方還挺多的 基本上就是算出這class唯一的數字
像是dictionary 當key時就會用到 GetHashCode 來比對

最後要比對都會是呼叫
public bool Equals(Employee other)
然後就看比對邏輯就可以自己寫囉

延伸試煉

C# GetHashCode()系列文

結束試煉

現在這些IDE都會幫忙產生了 真方便

C# Equality and Hashcodes
https://www.youtube.com/watch?v=QhpZxeAlX5w

https://github.com/JasperKent/Equality-And-Hashcodes

If You Implement Iequatable T You Still Must Override Object S Equals And Gethashcode
https://blog.paranoidcoding.com/2009/01/15/if-you-implement-iequatable-t-you-still-must-override-object-s-equals-and-gethashcode.html

void Main()
{
    var a = new Employee { Id = 1, Name = "a" };
    var b = new Employee { Id = 1, Name = "a" };
    var c = new List<Employee> { a, b };
    //var d = c.Distinct().Dump();
    var d = c.Distinct(new EmployeeEqualityComparer()).Dump();
    a.Equals(b).Dump();
    (a == b).Dump();

}
class EmployeeEqualityComparer : IEqualityComparer<Employee>
{

    public bool Equals(Employee x, Employee y)
    {
        return (x.Id == y.Id && x.Name == y.Name);
    }

    public int GetHashCode(Employee obj)
    {
        int hashCode = -1919740922;
        hashCode = hashCode * -1521134295 + obj.Id.GetHashCode();
        hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(obj.Name);
        return hashCode;
    }

}
class Employee//: IEquatable<Employee>
{
    public int Id { get; set; }
    public string Name { get; set; }

    //public bool Equals(Employee other)
    //{
    //	if (ReferenceEquals(other, null))
    //		return false;
    //		
    //	return (Id == other.Id && Name == other.Name);
    //}
    // 可以不 override 
    //	public override bool Equals(object obj)
    //	{
    //		Employee employee = (Employee)obj;
    //		return (Id == employee.Id && Name == employee.Name);
    //	}
    //
    //
    //
    //// 可以不 override 
    //public override int GetHashCode()
    //{
    //	int hashCode = -1919740922;
    //	hashCode = hashCode * -1521134295 + Id.GetHashCode();
    //	hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(Name);
    //	return hashCode;
    //}
    //
    //	public int GetHashCode(Employee obj)
    //	{
    //		throw new NotImplementedException();
    //	}
    //
    //	public static bool operator ==(Employee obj1, Employee obj2)
    //	{
    //		if (ReferenceEquals(obj1, obj2)) return true;
    //		if (ReferenceEquals(obj1, null)) return false;
    //		if (ReferenceEquals(obj2, null)) return false;
    //		return obj1.Equals(obj2);
    //	}
    //	public static bool operator !=(Employee obj1, Employee obj2) => !(obj1 == obj2);
}

如果內容有誤請多鞭策謝謝