1

I have a simple, generic container class in C# that looks something like this -

public class Point<T>
{
    public T Data { get; }
    public string ID { get; }

    public Point(T x, string id)
    {
        this.Data = x;
        this.ID = id;
    }
}

I would like all Point objects to be compared solely based on the ID property. My question is - how can I implement IEquatable<Point> such that any Point objects can be compared to each other? For example, Point<int> could be compared to Point<decimal>, and the objects would be considered equal if their ID properties were identical.

Treker
  • 386
  • 2
  • 9

1 Answers1

3

A simple way is to extract a non-generic interface. Though it is not required for IEquatable, you can even include the Data property in this interface.

public interface IPoint {
    object? Data { get; }
    string ID { get; }
}

public class Point<T>: IPoint, IEquatable<IPoint> {
    public T Data { get; }
    
    object? IPoint.Data => Data;
    
    public string ID { get; }

    public Point(T x, string id) {
        this.Data = x;
        this.ID = id;
    }
    
    public bool Equals(IPoint? other) => ID == other?.ID;
    
    public override bool Equals(object? obj) {
        return Equals(obj as IPoint);
    }

    public override int GetHashCode() {
        return ID.GetHashCode();
    }
}

Though a caveat is that other code now can provide their own implementation of IPoint and their own implementation of IEquatable<IPoint>, that isn't consistent with your Point<T>. This could cause confusing behaviours such as implA.Equals(implB) not agreeing with implB.Equals(implA).

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Could it also work if I extracted a non-generic abstract class with the Id property? – Treker Feb 12 '23 at 03:49
  • 1
    @Treker Yes, but you can't include the `Data` property in it, and interfaces are generally more flexible - a class can only have one superclass, but can implement multiple interfaces. – Sweeper Feb 12 '23 at 03:51