I've just started to program in C# last year and I'm still learning the language. I have a question regarding readonly struct type and equality comparison methods.
When creating a struct in C#, I know it's usually considered to be a best practice to implement IEquatable as the default reflection-based comparison is very slow. I also learned that in C# 7.2 and later we can define readonly structs and for these types, we can also use in parameter to avoid unnecessary copying.
As structs are usually defined as immutable readonly types, I suppose it's not unusual to define Equals methods for read only structs.
Given the above facts, however, what I'm wondering is if there is a good effective way to implement equality comparison methods for them. My point is that none of these equality methods and operators actually need to modify parameters so I want to utilize in parameters somehow to save unnecessary copying.
Below is my attempt at doing this:
public readonly struct Point : IEquatable<Point>
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
// Explicitly implementing IEquatable<Point> and delegating to an Equals method taking in param.
bool IEquatable<Point>.Equals(Point other) => Equals(other);
public bool Equals(in Point other) => X == other.X && Y == other.Y;
public override bool Equals(object? obj) => obj is Point other && Equals(other);
public static bool operator ==(in Point left, in Point right) => left.Equals(right);
public static bool operator !=(in Point left, in Point right) => !left.Equals(right);
public override int GetHashCode() => HashCode.Combine(X, Y);
public override string ToString() => $"Point({X}, {Y})";
}
The above certainly works but I think it's not perfect solution as copying is still needed if it's called through the IEquatable interface. Note that I can't just implement IEquatable implicitly with in param as Equal method taking in modifier is considered to have different signature and treated as an overload.
Is there a known best practice to implement this correctly?
What I would really like to know is if there is a known best practices and patterns to effectively implement equality for such readonly structs. Especially I'm interested in a way to properly utilize in parameter modifier for implementing Equality comparison methods.
So far I haven't found any satisfactory answer on the Web, and I also checked some source codes of the core library. For example, System.DateTime is now defined as readonly struct and it's rather big, but in parameter isn't utilized t here. (Existing type might well need to keep compatibility, I know they often need to have compromise, though.)
Note that the Point struct defined above is small composing only two 32 bit slot, so copying might not actually be a big problem here, but it's just meant for a simple illustrative example.
Update for .NET6(C# 10)
Now that C#10 has officially been released, the original question has become almost obsolete. We can now create such a read only struct as an readonly record struct. Of course depending of your model, it can also defined as a normal refence type record (class).
https://devblogs.microsoft.com/dotnet/welcome-to-csharp-10/
The point is that optimized equality comparison methods and operators are automatically generated for record types.
public readonly record struct Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}