参照:

生成 C# Equals 和 GetHashCode 方法重写 - Visual Studio (Windows) | Microsoft Learn

如何修改字符串内容 - C# | Microsoft Learn

在C#中,Equals 和 GetHashCode 方法用于对象的比较和哈希值计算。它们在值类型和值类型的行为上有所不同。

值类型(Value Types)

  1. Equals 方法:

    • 默认实现: 对于值类型,Equals 方法默认比较对象的值,而不是它们的引用。比如,对于结构体(struct)类型,Equals 方法会比较两个结构体的每个字段的值是否相等。
    • 可以覆盖: 值类型可以覆盖 Equals 方法以实现自定义的比较逻辑。
  2. GetHashCode 方法:

    • 默认实现: 值类型的 GetHashCode 方法默认基于对象的字段值生成哈希码。这通常会用到 GetHashCode 方法的字段值,并确保相同值的对象具有相同的哈希码。
    • 建议覆盖: 自定义值类型时,建议覆盖 GetHashCode 方法,以确保哈希码在逻辑上与 Equals 方法一致。特别是如果值类型用于哈希集合(如 HashSet<T>)或字典(如 Dictionary<TKey, TValue>)中,正确的 GetHashCode 实现非常重要。

引用类型(Reference Types)

  1. Equals 方法:

    • 默认实现: 对于引用类型,Equals 方法默认比较对象的引用是否相同,也就是比较对象的内存地址。如果需要比较对象的内容,需要覆盖 Equals 方法。
    • 可以覆盖: 引用类型通常会覆盖 Equals 方法以实现基于内容的比较逻辑,而不是基于引用。
  2. GetHashCode 方法:

    • 默认实现: 引用类型的 GetHashCode 方法默认基于对象的引用生成哈希码。这通常是内存地址的哈希值。
    • 建议覆盖: 当覆盖 Equals 方法时,也应当相应地覆盖 GetHashCode 方法,以保证相等的对象具有相同的哈希码。这对使用哈希集合或字典中的对象尤为重要

重写Equals 和 GetHashCode 实现

目的:有时候我们需要比较的是两个对象各属性值是否相等而不是比较内存地址是否也一样。这时候我们需要对Equals 和 GetHashCode 进行重写

重写前

using System.Linq;
List<Student> list = new List<Student>(){
    new Student(1,new Card(1,"小明")),new Student(2,new Card(1,"小明")),new Student(3,new Card(3,"小华")),new Student(4,new Card(2,"小李"))
};
var list2 = list.Select(s=>s.MyCard).Distinct().ToList();
Console.WriteLine();
public class Card
{
    public string Str { get; set; }
    public int Number { get; set; }
    public Card(int num,string strs){
        Str = strs;
        Number = num;
    }
    
}
public class Student
{
    public int No { get; set; }
    public Card MyCard { get; set; }
    public Student(int num, Card card)
    {
        MyCard = card;
        No = num;
    }
}

list2:出现两个小明

 

重写后

using System.Linq;
List<Student> list = new List<Student>(){
	new Student(1,new Card(1,"小明")),new Student(2,new Card(1,"小明")),new Student(3,new Card(3,"小华")),new Student(4,new Card(2,"小李"))
};
var list2 = list.Select(s=>s.MyCard).Distinct().ToList();
Console.WriteLine();
public class Card
{
	public string Str { get; set; }
	public int Number { get; set; }
	public Card(int num,string strs){
		Str = strs;
		Number = num;
	}
	public override int GetHashCode()
	{
		return Number.GetHashCode();
	}
	public override bool Equals(object obj)
	{
		return Equals(obj  as Card);
	}
	public bool Equals(Card card)
	{
		return card != null &&
			   Number == card.Number;
			   
	}
}
public class Student
{
	public int No { get; set; }
	public Card MyCard { get; set; }
	public Student(int num, Card card)
	{
		MyCard = card;
		No = num;
	}
}

  list2结果:

 结论:对于引用类的集合,distinct是没办法去重的。因为distinct调用的是默认的Equals的方法,比较的是引用地址。引用地址不一样,即使各个属性一样,也是不会相等。

破解引用类型集合distinct不生效的方式有两种,第一种如上重写默认的Equals的方法,第二种是定义一个新的属性比较的Compare的方法,作为Distinct的参数

list = list.Distinct((a, b) => a.Age == b.Age && a.Name == b.Name).ToList();

性能比较:C# Linq 的三种去重方式(Distinct)_linq distinct-CSDN博客

因为我们重写了Card的

string 是一个特殊的引用类型。它被设计为不可变的(immutable),这意味着一旦创建了 string 对象,其内容不能被更改,如果有新的。

参照:字符串 - C# 编程指南 | Microsoft Learn

string 类型的 Equals 和 GetHashCode 实现

  1. Equals 方法:

    • 实现: string 类型的 Equals 方法被重写(override)以比较两个字符串的内容,而不是它们的引用。具体来说,它会检查两个字符串的字符序列是否相同。
    • 逻辑: 方法会比较两个字符串的长度和每个字符。如果长度相同且每个字符都相等,则 Equals 方法返回 true,否则返回 false
    public override bool Equals(object obj)
    {
        if (obj is string otherString)
        {
            return StringComparer.Ordinal.Equals(this, otherString);
        }
        return false;
    }
  2. GetHashCode 方法:

    • 实现: string 类型的 GetHashCode 方法基于字符串的内容生成哈希码。它会使用字符串中的字符序列来计算哈希值,以确保相同内容的字符串具有相同的哈希码。
    • 逻辑: 通常,GetHashCode 使用字符串中的字符和其位置来计算哈希值。C# 中,string 的哈希算法依赖于字符内容和排序。由于字符串是不可变的,这样的实现保证了相同的字符串内容总是会生成相同的哈希码。
    public override int GetHashCode()
    {
        // 使用字符序列生成哈希码的实现
    }