C产品如何满足特定用户需求?
- 内容介绍
- 文章标签
- 相关推荐
本文共计1034个文字,预计阅读时间需要5分钟。
在+C语言中,以下是一个简单的代码示例,用于输出在+C语言中:
常见错误现象:new Person("Alice", 30).Equals(new Person("Alice", 30)) 返回 false,让人误以为“没写对”,其实是根本没重写。
- 必须同时重写
Equals(object)和GetHashCode(),否则字典、哈希集合会出问题 - 如果类型是
struct,默认已按字段值比较,但建议仍显式实现以明确语义 - 重写
Equals时,别忘了先做null检查和类型检查,否则可能抛NullReferenceException或静默返回false
重写 Equals 的标准写法(含类型安全)
最稳妥的模式是:先判断是否为 null,再用 as 转型并判空,避免 is + 强转带来的两次类型检查。
public override bool Equals(object obj) { var other = obj as Person; if (other is null) return false; return Name == other.Name && Age == other.Age; }
注意点:
- 别用
obj is Person other后直接访问other——当obj是子类实例时,可能绕过你本意的比较逻辑 - 字段比较要用
==还是Equals?对string推荐用string.Equals(a, b, StringComparison.Ordinal),避免空引用;对数值、枚举等值类型,==更快也更清晰 - 如果类有继承关系,且子类可能参与比较,得考虑是否调用
base.Equals,并确保父类的Equals实现是可靠的
GetHashCode 必须和 Equals 保持一致
GetHashCode 不是用来“生成唯一 ID”的,而是为了在哈希容器中快速分桶。只要 Equals 返回 true,两个对象的 GetHashCode 就必须相同;反过来不强制要求。
典型翻车现场:Dictionary<Person, string> 里加了一个 Person,之后改了它的 Name 字段,再用原对象去查——找不到。因为哈希码变了,桶位置已失效。
- 哈希码应仅基于
Equals中用到的、不可变(或至少在字典生命周期内不变)的字段计算 - 推荐用
HashCode.Combine(field1, field2)(.NET Core 2.1+),它比手写xor或乘加更均衡、不易冲突 - 如果用了可变字段(比如临时缓存字段),要么不参与哈希计算,要么文档里明确警告“修改后不得用于哈希容器”
record 类型能省事,但不等于不用思考
.NET 5+ 的 record 默认实现了基于属性的 Equals 和 GetHashCode,看起来一劳永逸。但它只对 record 自身声明的 init 或 get-only 属性生效。
容易被忽略的细节:
- 如果属性类型是普通 class(比如
Address),而它没重写Equals,那整个 record 的相等性还是退化为引用比较 -
record struct行为不同:它默认按所有字段位比较,包括引用类型字段的地址值,这通常不是你想要的 - 用
with表达式创建副本时,如果内部字段是可变引用类型,副本和原对象仍共享同一实例——相等性判断看似成立,实际数据可能被意外修改
真正麻烦的从来不是语法怎么写,而是想清楚“什么才算相等”:是字段值完全一致?忽略大小写?容忍浮点误差?还是业务上认为 ID 相同即相等?这些决策一旦定下,Equals 和 GetHashCode 就得严格对齐,而且很难再改。
本文共计1034个文字,预计阅读时间需要5分钟。
在+C语言中,以下是一个简单的代码示例,用于输出在+C语言中:
常见错误现象:new Person("Alice", 30).Equals(new Person("Alice", 30)) 返回 false,让人误以为“没写对”,其实是根本没重写。
- 必须同时重写
Equals(object)和GetHashCode(),否则字典、哈希集合会出问题 - 如果类型是
struct,默认已按字段值比较,但建议仍显式实现以明确语义 - 重写
Equals时,别忘了先做null检查和类型检查,否则可能抛NullReferenceException或静默返回false
重写 Equals 的标准写法(含类型安全)
最稳妥的模式是:先判断是否为 null,再用 as 转型并判空,避免 is + 强转带来的两次类型检查。
public override bool Equals(object obj) { var other = obj as Person; if (other is null) return false; return Name == other.Name && Age == other.Age; }
注意点:
- 别用
obj is Person other后直接访问other——当obj是子类实例时,可能绕过你本意的比较逻辑 - 字段比较要用
==还是Equals?对string推荐用string.Equals(a, b, StringComparison.Ordinal),避免空引用;对数值、枚举等值类型,==更快也更清晰 - 如果类有继承关系,且子类可能参与比较,得考虑是否调用
base.Equals,并确保父类的Equals实现是可靠的
GetHashCode 必须和 Equals 保持一致
GetHashCode 不是用来“生成唯一 ID”的,而是为了在哈希容器中快速分桶。只要 Equals 返回 true,两个对象的 GetHashCode 就必须相同;反过来不强制要求。
典型翻车现场:Dictionary<Person, string> 里加了一个 Person,之后改了它的 Name 字段,再用原对象去查——找不到。因为哈希码变了,桶位置已失效。
- 哈希码应仅基于
Equals中用到的、不可变(或至少在字典生命周期内不变)的字段计算 - 推荐用
HashCode.Combine(field1, field2)(.NET Core 2.1+),它比手写xor或乘加更均衡、不易冲突 - 如果用了可变字段(比如临时缓存字段),要么不参与哈希计算,要么文档里明确警告“修改后不得用于哈希容器”
record 类型能省事,但不等于不用思考
.NET 5+ 的 record 默认实现了基于属性的 Equals 和 GetHashCode,看起来一劳永逸。但它只对 record 自身声明的 init 或 get-only 属性生效。
容易被忽略的细节:
- 如果属性类型是普通 class(比如
Address),而它没重写Equals,那整个 record 的相等性还是退化为引用比较 -
record struct行为不同:它默认按所有字段位比较,包括引用类型字段的地址值,这通常不是你想要的 - 用
with表达式创建副本时,如果内部字段是可变引用类型,副本和原对象仍共享同一实例——相等性判断看似成立,实际数据可能被意外修改
真正麻烦的从来不是语法怎么写,而是想清楚“什么才算相等”:是字段值完全一致?忽略大小写?容忍浮点误差?还是业务上认为 ID 相同即相等?这些决策一旦定下,Equals 和 GetHashCode 就得严格对齐,而且很难再改。

