0

I have a Student class that has an object of class Name.

At this point, the equality check of the Student class returns false, and I don't know why.

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());

Of course, doing the equality check this way will return true.

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

Console.WriteLine(s1.Equals(s2).ToString());

Can you tell me what I'm doing wrong?

isakgo_
  • 750
  • 4
  • 15
  • 1
    You should use `System.Collections.Generic.EqualityComparer.Default.Equals(o1, o2)` instead of `==`. EqualityComparer tries to find best equality method for given class, while `==` just compares by reference if it's not overridden – JL0PD Jul 11 '22 at 03:11
  • @JL0PD That "best equality method" is _always_ `Equals()`, so need to whip out the default equality comparer, just call the method directly. – Etienne de Martel Jul 11 '22 at 03:21
  • @EtiennedeMartel, overload for equals depends on whether type implements `IEquatable`. If type implements interface, then check will be done with `IEquatable.Equals(T other)`. If type doesn't implement interface, then `Object.Equals(object other)` is used. So it's not "always Equals" – JL0PD Jul 11 '22 at 05:00
  • you can use c# records, they are much simpler and come with equality comparators – Lokanath Jul 11 '22 at 07:17

2 Answers2

3

So, == by default for reference types just compares references. Implementing IEquatable<T> does not overload == automatically, you have to do it yourself. string does overload that operator, so it works in Name, but your Name class doesn't, so in Student, the default behaviour is used, which is equivalent to ReferenceEquals().

Implementing IEquatable<T> correctly also means you need to override Equals(object) and GetHashCode(), which you don't do here.

Etienne de Martel
  • 34,692
  • 8
  • 91
  • 111
2

Solution 1 (Recommended)

Since you had implemented IEquatable interface for Name class, just calls .Equals() in Student class.

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.Equals(other.Name); // Replaces with .Equals
    }
}

Sample .NET Fiddle (Solution 1)


Solution 2

Or you need to implement operator overloading for the == and != operators.

If you use == operator in:

public class Student : IEquatable<Student>
{
    ...

    public bool Equals(Student other)
    {
        ...

        return Name == (other.Name);
    }
}
public class Name : IEquatable<Name>
{
    ...
    
    public static bool operator == (Name a, Name b)
    {
        return a.First == b.First;
    }
    
    public static bool operator != (Name a, Name b)
    {
        return a.First != b.First;
    }
}

Sample .NET Fiddle (Solution 2)

Yong Shun
  • 35,286
  • 4
  • 24
  • 46