Anonymous classes (a.k.a. anonymous types) are implicitly created and used by the C# compiler for code like this:
var obj = new { X1 = 0, X2 = 1 };
They have value equality semantics, i.e., the method bool Equals(object? obj) from the class object is overridden to perform field-by-field comparison. A typical C# class or struct T with value equality semantics implements the interface System.IEquatable<T>. The official guideline for how to implement equality also asserts that "both classes and structs require the same basic steps for implementing equality:
Override the
virtual Object.Equals(Object)method. In most cases, your implementation ofbool Equals(object obj)should just call into the type-specificEqualsmethod that is the implementation of theSystem.IEquatable<T>interface. (See step 2.)Implement the
System.IEquatable<T>interface by providing a type-specificEqualsmethod."
However, anonymous classes are compiled to something like this:
internal sealed class AnonymousType<T1, T2> {
public T1 X1 { get; }
public T2 X2 { get; }
public AnonymousType(T1 x1, T2 x2) { X1 = x1; X2 = x2; }
public override bool Equals(object? obj) {
var other = obj as AnonymousType<T1, T2>;
return this == other || (other != null &&
EqualityComparer<T1>.Default.Equals(X1, other.X1) &&
EqualityComparer<T2>.Default.Equals(X2, other.X2));
}
public override int GetHashCode() { /* code omitted */ }
public override string ToString() { /* code omitted */ }
}
I would expect them to look more like this instead:
internal sealed class AnonymousType<T1, T2> : IEquatable<AnonymousType<T1, T2>> {
public T1 X1 { get; }
public T2 X2 { get; }
public AnonymousType(T1 x1, T2 x2) { X1 = x1; X2 = x2; }
public bool Equals(AnonymousType<T1, T2>? other) =>
this == other || (other != null &&
EqualityComparer<T1>.Default.Equals(X1, other.X1) &&
EqualityComparer<T2>.Default.Equals(X2, other.X2));
public override bool Equals(object? obj) =>
Equals(obj as AnonymousType<T1, T2>);
public override int GetHashCode() { /* code omitted */ }
public override string ToString() { /* code omitted */ }
}
Perhaps, I would also add the == and != operators. (The guidelines say this is "optional but recommended".)
Is there any specific reason why anonymous classes do not implement IEquatable<T>, breaking the aforementioned guidelines?