相等性与哈希(值与引用)
理解引用相等性与值相等性,正确重写 Equals/GetHashCode,实现 IEquatable ,并在集合和字典中使用自定义比较器。
相等性与哈希(值与引用) 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 3 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 3 节课。
相等性基础
目标:让相等性行为符合您的预期。
- 引用相等性与值相等性
- Equals + GetHashCode 契约
- 使用 IEquatable<T> 提高速度
- 无法修改类型时使用自定义比较器
引用相等性的陷阱
类默认使用引用相等性。两个数据相同但彼此独立的对象并不相等,除非您实现值相等性。
using System;
using System.Collections.Generic;
public sealed class Point // no overrides
{
public int X;
public int Y;
public Point(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
Point a = new Point(1, 2);
Point b = new Point(1, 2);
Console.WriteLine("a == b ? " + (a == b)); // reference equality: False
Console.WriteLine("a.Equals(b) ? " + a.Equals(b)); // False
HashSet<Point> set = new HashSet<Point>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // False (unexpected)
}
}
已实现值相等性
实现 IEquatable<T>,重写 Equals 和 GetHashCode。哈希值必须与相等性匹配:相等的对象 → 哈希值相同。
using System;
using System.Collections.Generic;
public sealed class ValuePoint : IEquatable<ValuePoint>
{
public int X;
public int Y;
public ValuePoint(int x, int y){ X = x; Y = y; }
public bool Equals(ValuePoint other)
{
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
return X == other.X && Y == other.Y;
}
public override bool Equals(object obj)
{
return Equals(obj as ValuePoint);
}
public override int GetHashCode()
{
// Simple, stable combination (avoid randomness)
unchecked
{
int hash = 17;
hash = hash * 31 + X.GetHashCode();
hash = hash * 31 + Y.GetHashCode();
return hash;
}
}
}
public class Program
{
public static void Main(string[] args)
{
ValuePoint a = new ValuePoint(1, 2);
ValuePoint b = new ValuePoint(1, 2);
Console.WriteLine("a.Equals(b)? " + a.Equals(b)); // True
HashSet<ValuePoint> set = new HashSet<ValuePoint>();
set.Add(a);
Console.WriteLine("Contains b? " + set.Contains(b)); // True (value semantics)
}
}
自定义比较器
当无法修改类型本身时,将自定义 IEqualityComparer<T> 传递给 HashSet/Dictionary。
using System;
using System.Collections.Generic;
public sealed class Person // imagine from a library; cannot edit
{
public string Name;
public int BirthYear;
public Person(string name, int year){ Name = name; BirthYear = year; }
}
public sealed class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person a, Person b)
{
if (ReferenceEquals(a, b)) return true;
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) return false;
return a.Name == b.Name && a.BirthYear == b.BirthYear;
}
public int GetHashCode(Person p)
{
if (ReferenceEquals(p, null)) return 0;
unchecked
{
int h = 23;
h = h * 31 + (p.Name == null ? 0 : p.Name.GetHashCode());
h = h * 31 + p.BirthYear.GetHashCode();
return h;
}
}
}
public class Program
{
public static void Main(string[] args)
{
Person p1 = new Person("Ada", 1815);
Person p2 = new Person("Ada", 1815);
HashSet<Person> set = new HashSet<Person>(new PersonComparer());
set.Add(p1);
Console.WriteLine("Contains p2? " + set.Contains(p2)); // True via comparer
}
}
契约与陷阱
规则:
- 如果 Equals(a,b) 为 true ⇒ GetHashCode(a) == GetHashCode(b)
- 相等性应满足自反性、对称性和传递性
- 在可能的情况下,使用不可变字段计算哈希值
- 不要在 GetHashCode 中使用随机值
结构体与类的说明
结构体默认按字段进行比较(值语义)。类默认按引用进行比较,除非您实现值相等性。
using System;
public struct PointS // struct: value type
{
public int X;
public int Y;
public PointS(int x, int y){ X = x; Y = y; }
}
public class Program
{
public static void Main(string[] args)
{
PointS a = new PointS(1, 2);
PointS b = new PointS(1, 2);
Console.WriteLine("Struct equality: " + a.Equals(b)); // True by default (field-wise)
}
}
集合的相等性契约
回顾
回顾:类需要实现 Equals/GetHashCode(通常还需要 IEquatable<T>)才能具有值语义;类型无法编辑时,请使用自定义比较器。
常见问题解答
「相等性与哈希(值与引用)」课时是免费的吗?
是的 — 「相等性与哈希(值与引用)」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 3 节课。
「相等性与哈希(值与引用)」这节课中我会学到什么?
理解引用相等性与值相等性,正确重写 Equals/GetHashCode,实现 IEquatable ,并在集合和字典中使用自定义比较器。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 3 节。
「相等性与哈希(值与引用)」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。