首页 > 编程语言 >C# Equals 和 GetHashCode 方法认知及Distinct方法解析

C# Equals 和 GetHashCode 方法认知及Distinct方法解析

时间:2024-07-09 10:31:00浏览次数:17  
标签:C# Equals Distinct 哈希 GetHashCode new public Card

参照:

生成 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()
    {
        // 使用字符序列生成哈希码的实现
    }

 

标签:C#,Equals,Distinct,哈希,GetHashCode,new,public,Card
From: https://www.cnblogs.com/summerZoo/p/18291177

相关文章

  • [namespace hdk] Balanced_tree 整合
    代码#include<bits/stdc++.h>usingnamespacestd;namespacehdk{ namespacebalanced_tree{ constintN=2000001,inf=114514191; classsplay{ private: introot,tot; structtree{ intw; intcnt,size; intfa,son[2]; }t[N];......
  • VsCode崩溃无法打开解决办法
    今天打开电脑点开vscode突然不能使用了每次点开都闪退,于是查找解决办法直接重启电脑这个方法没有解决问题删除软件重新安装软件软件删干净之前保存下自己的配置文件比如扩展和settings.json和keybinding.jsonMac系统在/Users/用户名/.vscode/extensions和/Us......
  • 经典C语言笔试面试题目
    01.请填写bool,float,指针变量与“零值”比较的if语句。提示:这里“零值”可以是0,0.0,FALSE或者“空指针”。例如intn与“零值”比较的if语句为:if(n==0)if(n!=0)以此类推。请写出boolflag与“零值”比较的if语句:if(flag){}if(!fl......
  • 机器学习策略篇:快速搭建你的第一个系统,并进行迭代(Build your first system quickly, t
    快速搭建的第一个系统,并进行迭代如果正在考虑建立一个新的语音识别系统,其实可以走很多方向,可以优先考虑很多事情。比如,有一些特定的技术,可以让语音识别系统对嘈杂的背景更加健壮,嘈杂的背景可能是说咖啡店的噪音,背景里有很多人在聊天,或者车辆的噪音,高速上汽车的噪音或者其他类型......
  • 使用react物料
    1.win安装node.js2.安装axios报错,进入到C:\ProgramFiles\nodejs\node_modules\npm目录 成功!安装json-servernpminstalljson-server-g   原文:https://ice.work/docs/guide/about  ......
  • Oracle数据库使用expdp/impdp导出导入数据
    背景:正式环境数据同步到测试环境,数据库名:MYDB,正式、用户:MYUSER(必须拥有SYS权限)。1、正式环境备份数据库(1)正式服务器上,cmd输入sqlplus,使用MYUSER账户登录(2)创建一个自定义的目录,用于存放导出的数据createdirectoryDATA_OUT_FILEas'E:\app\Administrator\admin\MYDB\my_dir\'......
  • 处理Keras中的AttributeError: ‘NoneType‘ object has no attribute ‘XYZ‘
    处理Keras中的AttributeError:'NoneType'objecthasnoattribute'XYZ'......
  • 应对PyTorch中的TypeError: ‘module‘ object is not callable
    应对PyTorch中的TypeError:'module'objectisnotcallable......
  • which命令、可执行文件
    用途:查找可执行文件并显示所在的位置 which会到哪里去找可执行权限?which——搜索范围由PATH环境变量指定;PATH环境变量定义了which去哪里查找命令(shell解析器会去哪里查找命令)可执行文件:可以运行的文件 、执行execute 、可执行的executable二进制是可以直接运......
  • 备份脚本backup_log.sh、计划任务
    脚本+计划任务 = 解放了劳动力,提升效率--》实现了自动化操作编写一个脚本backup_log.sh实现备份/var/log目录下的所有日志文件到/backup目录下,要求文件名是包含当天日期,精确到秒,文件名例如:20240308151520-log.tar.gz。同时要求删除/backup目录下七天前的备份文件,只保留最......