0Pricing
C# Academy · 课时

重载比较运算符

实现 ==、!= 和排序运算符。

重载比较运算符 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 重载算术运算符
  2. 重载比较运算符
  3. 用户定义的转换
  4. 运算符重载最佳实践
← 返回 C# Academy