C#教程

关注公众号 jb51net

关闭
首页 > 软件编程 > C#教程 > C#实现值相等Value Equality

C#中实现值相等(Value Equality)的详细步骤

作者:无风听海

在 C# 中,相等并不是一个简单的问题,很多开发者认为重写 Equals 就够了,但在真实系统中,错误或不完整的相等实现会导致一些bug,本文将从底层机制出发,给出标准、完整、可复用的值相等实现步骤,需要的朋友可以参考下

一、为什么“值相等”是一个需要认真对待的问题

在 C# 中,相等并不是一个简单的问题
很多开发者认为重写 Equals 就够了,但在真实系统中,错误或不完整的相等实现会导致:

这背后的原因在于:

.NET 的相等语义是一个由多个方法和接口共同构成的协作体系,而不是单一方法。

本文将从底层机制出发,给出标准、完整、可复用的实现步骤

二、相等的两种语义:引用相等 vs 值相等

在 .NET 中,存在两种本质不同的“相等”:

1. 引用相等(Reference Equality)

object.ReferenceEquals(a, b)

2. 值相等(Value Equality)

var a = new Person("Tom", 18);
var b = new Person("Tom", 18);

ReferenceEquals(a, b); // false
a.Equals(b);           // true(如果实现了值相等)

三、.NET 相等体系的整体结构

实现值相等,必须理解以下四个关键成员的职责分工:

成员角色
IEquatable<T>.Equals(T)类型安全、性能最优的相等判断
object.Equals(object)所有 .NET API 的统一入口
GetHashCode()哈希集合的基础
== / != 运算符语法层面的相等判断(可选)

一个正确的值相等实现,必须保证这些成员在语义上一致。

四、类(引用类型)实现值相等的标准步骤

以下步骤适用于绝大多数引用类型(class)

Step 1:明确“相等”的语义(设计阶段)

首先必须回答一个设计问题:

哪些字段决定两个对象在业务语义上是“相等”的?

例如:

Person 相等 ⇔ Name 和 Age 都相等

这一步没有代码,但至关重要。

Step 2:实现IEquatable<T>.Equals(T other)(核心步骤)

public sealed class Person : IEquatable<Person>
{
    public string Name { get; }
    public int Age { get; }

    public bool Equals(Person other)
    {
        if (other is null) return false;
        return Name == other.Name && Age == other.Age;
    }
}

为什么这是核心?

IEquatable<T> 是值相等的主入口。

Step 3:重写object.Equals(object obj)(必须)

public override bool Equals(object obj)
{
    return Equals(obj as Person);
}

为什么必须?

规范要求

Equals(object) 必须委托给 Equals(T),而不是重复实现逻辑。

Step 4:重写GetHashCode()(必须)

public override int GetHashCode()
{
    return HashCode.Combine(Name, Age);
}

必须遵守的核心约束

如果 a.Equals(b) 为 true,
那么 a.GetHashCode() 必须等于 b.GetHashCode()。

否则:

实践建议

Step 5(可选但推荐):重载== / !=运算符

public static bool operator ==(Person left, Person right)
{
    return object.Equals(left, right);
}

public static bool operator !=(Person left, Person right)
{
    return !object.Equals(left, right);
}

说明

五、完整标准实现模板

public sealed class Person : IEquatable<Person>
{
    public string Name { get; }
    public int Age { get; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public bool Equals(Person other)
    {
        if (other is null) return false;
        return Name == other.Name && Age == other.Age;
    }

    public override bool Equals(object obj)
        => Equals(obj as Person);

    public override int GetHashCode()
        => HashCode.Combine(Name, Age);

    public static bool operator ==(Person left, Person right)
        => object.Equals(left, right);

    public static bool operator !=(Person left, Person right)
        => !object.Equals(left, right);
}

六、结构体(值类型)的补充说明

public struct Point : IEquatable<Point>
{
    public int X;
    public int Y;

    public bool Equals(Point other)
        => X == other.X && Y == other.Y;

    public override bool Equals(object obj)
        => obj is Point p && Equals(p);

    public override int GetHashCode()
        => HashCode.Combine(X, Y);
}

七、record:值相等的语言级支持(C# 9+)

public record Person(string Name, int Age);

编译器自动生成:

对于值对象(Value Object),record 是首选方案。

八、常见错误总结

九、结论

在 C# 中,实现值相等不是“重写一个方法”,
而是一个需要遵循明确步骤和约束的完整设计。

标准流程可以总结为:

  1. 明确定义相等语义
  2. 实现 IEquatable<T>.Equals(T)
  3. 重写 object.Equals(object)
  4. 重写 GetHashCode()
  5. (可选)重载 == / !=

这套实现,才能在语言层、集合层和框架层都保持一致、可靠的行为。

以上就是C#中实现值相等(Value Equality)的详细步骤的详细内容,更多关于C#实现值相等Value Equality的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文