You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#含自定义类成员的类相等性校验返回false问题排查

问题背景

定义了包含Name类成员的Student类,两个类都实现了IEquatable<T>接口做相等性判断,但出现不符合预期的行为:

  • 两个属性值完全一致的不同Student实例对比,返回false
  • 两个变量引用同一个Student实例时,对比返回true

相关实现代码如下:

public class Student : IEquatable<Student>
{
  public Name Name { get; }

  public Student(Name name) => Name = name;

  public bool Equals(Student other)
  {
    if (ReferenceEquals(this, other))
      return true;
    if (ReferenceEquals(other, null))
      return false;

    return Name == (other.Name);
  }
}

public class Name : IEquatable<Name>
{
  public string First { get; }

  public Name(string first) => First = first;

  public bool Equals(Name other)
  {
    if (ReferenceEquals(this, other))
      return true;
    if (ReferenceEquals(other, null))
      return false;

    return First == other.First;
  }
}

复现异常的测试代码:

var s1 = new Student(new Name("A"));
var s2 = new Student(new Name("A"));

Console.WriteLine(s1.Equals(s2).ToString()); // 输出False,不符合值相等预期

验证同引用场景正常的测试代码:

var s1 = new Student(new Name("A"));
var s2 = s1;

Console.WriteLine(s1.Equals(s2).ToString()); // 输出True
问题根因

错误出在Name类型的运算符调用逻辑上:

  • 你虽然为Name类实现了IEquatable<Name>接口的Equals值比较逻辑,但没有重载Name类的==运算符。
  • C#中引用类型的默认==运算符执行的是引用相等判断,即只有两个变量指向同一个堆内存实例时才返回true,不会自动调用你实现的IEquatable<Name>.Equals方法。
  • 你在Student.Equals中写的Name == (other.Name)走的就是默认的引用比较:测试用例中s1和s2持有的Name对象是分别通过new Name("A")创建的两个独立实例,引用地址不同,因此直接返回false,根本没有执行你写的First字段值对比逻辑。
  • 当s2 = s1时,两个变量不仅引用同一个Student实例,也自然引用同一个Name实例,所以引用判断返回true,和你的值比较预期巧合一致。
修复方式

两种方案都可以解决问题,推荐第二种做规范实现:

方案1:最小改动,直接调用Equals方法

把Student.Equals中的==比较替换为对Name.Equals的调用即可:

// 替换原来的 return Name == (other.Name);
return Name.Equals(other.Name);

如果存在Name属性为null的场景,补充空判断更稳妥:

if (ReferenceEquals(Name, null))
    return ReferenceEquals(other.Name, null);
return Name.Equals(other.Name);

方案2:规范实现,重载相等运算符

为实现了IEquatable<T>的类型重载==和!=运算符,保证运算符逻辑和Equals逻辑一致,这是C#自定义值相等类型的标准实践。
在Name类中补充以下代码:

public static bool operator ==(Name left, Name right)
{
    if (ReferenceEquals(left, null))
        return ReferenceEquals(right, null);
    return left.Equals(right);
}

public static bool operator !=(Name left, Name right)
{
    return !(left == right);
}

// 建议同时重写object类的Equals和GetHashCode,避免父类方法逻辑不一致
public override bool Equals(object obj)
{
    return Equals(obj as Name);
}

public override int GetHashCode()
{
    return First?.GetHashCode() ?? 0;
}

同样建议给Student类也补上对应的运算符重载和object.Equals、GetHashCode重写,避免其他场景下出现相等判断不一致的问题。补完后你原来写的Name == other.Name就会正确执行值比较逻辑。

内容的提问来源于stack exchange,提问作者na_sacc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 03:42:18