重载比较运算符
实现 ==、!= 和排序运算符。
重载比较运算符 是 CoddyKit 上的免费 C# Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
比较运算符与相等性
重载 == 时,还必须重载 !=,并且应重写 Equals 和 GetHashCode,以保持所有相等性形式的一致。C# 会在编译时强制执行运算符配对。
重载 == 与 !=
编译器要求同时定义 == 和 !=。每个运算符都会返回一个 bool,用于说明操作数是否被视为相等。
using System;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public static bool operator ==(Point a, Point b) => a.X == b.X && a.Y == b.Y;
public static bool operator !=(Point a, Point b) => !(a == b);
public override bool Equals(object obj) => obj is Point p && this == p;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Point(1, 2) == new Point(1, 2));
Console.WriteLine(new Point(1, 2) != new Point(3, 4));
}
}也始终重写 Equals
运算符会根据静态类型在编译时解析,而 Equals 是虚方法,会被集合使用。请重写 Equals,这样字典和 List.Contains 才能与您的 == 保持一致。
using System;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public static bool operator ==(Point a, Point b) => a.X == b.X && a.Y == b.Y;
public static bool operator !=(Point a, Point b) => !(a == b);
public override bool Equals(object obj) => obj is Point p && this == p;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
object a = new Point(1, 2);
object b = new Point(1, 2);
Console.WriteLine(a.Equals(b));
}
}GetHashCode 必须与相等性一致
如果两个值相等,它们就必须返回相同的哈希代码。否则,像 Dictionary 和 HashSet 这样的基于哈希的集合将无法正常工作。
using System;
using System.Collections.Generic;
public struct Point
{
public int X, Y;
public Point(int x, int y) { X = x; Y = y; }
public static bool operator ==(Point a, Point b) => a.X == b.X && a.Y == b.Y;
public static bool operator !=(Point a, Point b) => !(a == b);
public override bool Equals(object obj) => obj is Point p && this == p;
public override int GetHashCode() => HashCode.Combine(X, Y);
}
public class Program
{
public static void Main()
{
var set = new HashSet<Point> { new Point(1, 2) };
Console.WriteLine(set.Contains(new Point(1, 2)));
}
}排序运算符 < 与 >
您也可以重载关系运算符。与相等运算符一样,< 和 > 必须同时定义,<= 和 >= 也必须同时定义。
using System;
public struct Weight
{
public int Grams;
public Weight(int g) { Grams = g; }
public static bool operator <(Weight a, Weight b) => a.Grams < b.Grams;
public static bool operator >(Weight a, Weight b) => a.Grams > b.Grams;
public override string ToString() => Grams + "g";
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Weight(100) < new Weight(200));
Console.WriteLine(new Weight(300) > new Weight(200));
}
}引用类型的相等性
对于类,值相等意味着比较字段,而不是比较引用。请谨慎处理 null,以免在运算符内部抛出异常。
using System;
public class Person
{
public string Name;
public Person(string name) { Name = name; }
public static bool operator ==(Person a, Person b)
{
if (ReferenceEquals(a, b)) return true;
if (a is null || b is null) return false;
return a.Name == b.Name;
}
public static bool operator !=(Person a, Person b) => !(a == b);
public override bool Equals(object obj) => this == (obj as Person);
public override int GetHashCode() => Name?.GetHashCode() ?? 0;
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Person("Ann") == new Person("Ann"));
}
}各种形式之间的一致性
所有相等性路径都应保持一致:==、Equals 和哈希代码。请根据 Equals(或反过来)实现 ==,以便只有一个事实来源。
using System;
public struct Id
{
public int Value;
public Id(int value) { Value = value; }
public bool Equals(Id other) => Value == other.Value;
public override bool Equals(object obj) => obj is Id other && Equals(other);
public override int GetHashCode() => Value;
public static bool operator ==(Id a, Id b) => a.Equals(b);
public static bool operator !=(Id a, Id b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Id(7) == new Id(7));
Console.WriteLine(new Id(7).Equals(new Id(8)));
}
}集合中的运算符与 Equals
列表搜索使用 Equals 而不是 ==,因为它处理的是 object 引用。如果您只重载 == 而忘记 Equals,搜索就会使用引用身份,可能导致失败。
using System;
using System.Collections.Generic;
public struct Tag
{
public string Name;
public Tag(string name) { Name = name; }
public override bool Equals(object obj) => obj is Tag t && t.Name == Name;
public override int GetHashCode() => Name?.GetHashCode() ?? 0;
public static bool operator ==(Tag a, Tag b) => a.Equals(b);
public static bool operator !=(Tag a, Tag b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
var list = new List<Tag> { new Tag("red"), new Tag("blue") };
Console.WriteLine(list.Contains(new Tag("blue")));
}
}正确消除编译器警告
同时重写 Equals 和 GetHashCode 并配合运算符使用,并不只是礼节问题;如果您重载 == 却不重写 Equals,编译器就会发出警告。两者都实现可以消除警告并避免错误。
using System;
public struct Cell
{
public int Row, Col;
public Cell(int r, int c) { Row = r; Col = c; }
public override bool Equals(object obj) => obj is Cell c && c.Row == Row && c.Col == Col;
public override int GetHashCode() => HashCode.Combine(Row, Col);
public static bool operator ==(Cell a, Cell b) => a.Equals(b);
public static bool operator !=(Cell a, Cell b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
Console.WriteLine(new Cell(0, 0) == new Cell(0, 0));
}
}整合起来
一个行为良好的可相等类型会定义 == 和 !=,重写 Equals 和 GetHashCode,并让它们全部通过同一个比较逻辑运行。这是值语义类型的最佳实践。
using System;
using System.Collections.Generic;
public struct Color
{
public int R, G, B;
public Color(int r, int g, int b) { R = r; G = g; B = b; }
public override bool Equals(object obj) => obj is Color c && c.R == R && c.G == G && c.B == B;
public override int GetHashCode() => HashCode.Combine(R, G, B);
public static bool operator ==(Color a, Color b) => a.Equals(b);
public static bool operator !=(Color a, Color b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
var dict = new Dictionary<Color, string> { [new Color(255, 0, 0)] = "red" };
Console.WriteLine(dict[new Color(255, 0, 0)]);
}
}亲自尝试
实现一个完全一致的可相等结构体,并验证它可以同时作为字典键使用,也能与 == 运算符配合工作。
using System;
using System.Collections.Generic;
public struct GridPos
{
public int Row, Col;
public GridPos(int r, int c) { Row = r; Col = c; }
public override bool Equals(object obj) => obj is GridPos p && p.Row == Row && p.Col == Col;
public override int GetHashCode() => HashCode.Combine(Row, Col);
public static bool operator ==(GridPos a, GridPos b) => a.Equals(b);
public static bool operator !=(GridPos a, GridPos b) => !a.Equals(b);
}
public class Program
{
public static void Main()
{
var grid = new Dictionary<GridPos, string> { [new GridPos(1, 1)] = "player" };
Console.WriteLine(new GridPos(1, 1) == new GridPos(1, 1));
Console.WriteLine(grid[new GridPos(1, 1)]);
}
}快速检查
回顾重载相等运算符的规则。
回顾
重载比较运算符必须保持一致。
==和!=必须同时定义;</>也是如此。- 重写
Equals和GetHashCode,使其与==保持一致。 - 相等的值必须共享同一个哈希代码。
- 让所有相等性判断都通过同一个比较方法运行,以避免逐渐产生差异。
常见问题解答
「重载比较运算符」课时是免费的吗?
是的 — 「重载比较运算符」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「重载比较运算符」这节课中我会学到什么?
实现 ==、!= 和排序运算符。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。
「重载比较运算符」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。