I'm just wondering why designers of the language decided to implement Equals on anonymous types similarly to Equals
on value types. Isn't it misleading?
public class Person { public string Name { get; set; } public int Age { get; set; } } public static void ProofThatAnonymousTypesEqualsComparesBackingFields() { var personOne = new { Name = "Paweł", Age = 18 }; var personTwo = new { Name = "Paweł", Age = 18 }; Console.WriteLine(personOne == personTwo); // false Console.WriteLine(personOne.Equals(personTwo)); // true Console.WriteLine(Object.ReferenceEquals(personOne, personTwo)); // false var personaOne = new Person { Name = "Paweł", Age = 11 }; var personaTwo = new Person { Name = "Paweł", Age = 11 }; Console.WriteLine(personaOne == personaTwo); // false Console.WriteLine(personaOne.Equals(personaTwo)); // false Console.WriteLine(Object.ReferenceEquals(personaOne, personaTwo)); // false }
At first glance, all printed boolean values should be false. But lines with Equals
calls return different values when Person
type is used, and anonymous type is used.
Anonymous types provide a convenient way to encapsulate a set of read-only properties into a single object without having to explicitly define a type first. The type name is generated by the compiler and is not available at the source code level. The type of each property is inferred by the compiler.
Tradeoffs. You might want to always use ValueTuple over Tuple, and anonymous types, but there are tradeoffs you should consider. The ValueTuple types are mutable, whereas Tuple are read-only. Anonymous types can be used in expression trees, while tuples cannot.
The compiler generates a name for each anonymous type. If two or more anonymous type objects are defined in the same assembly and the sequence of properties are same in terms of names and types than the compiler treats both as same object instances of type.
The compiler gives them a name although your application cannot access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type, except that it cannot be cast to any type except for object.
Anonymous type instances are immutable data values without behavior or identity. It doesn't make much sense to reference-compare them. In that context I think it is entirely reasonable to generate structural equality comparisons for them.
If you want to switch the comparison behavior to something custom (reference comparison or case-insensitivity) you can use Resharper to convert the anonymous type to a named class. Resharper can also generate equality members.
There is also a very practical reason to do this: Anonymous types are convenient to use as hash keys in LINQ joins and groupings. For that reason they require semantically correct Equals
and GetHashCode
implementations.
For the why part you should ask the language designers...
But I found this in Eric Lippert’s article about Anonymous Types Unify Within An Assembly, Part Two
An anonymous type gives you a convenient place to store a small immutable set of name/value pairs, but it gives you more than that. It also gives you an implementation of Equals, GetHashCode and, most germane to this discussion, ToString. (*)
Where the why part comes in the note:
(*) We give you Equals and GetHashCode so that you can use instances of anonymous types in LINQ queries as keys upon which to perform joins. LINQ to Objects implements joins using a hash table for performance reasons, and therefore we need correct implementations of Equals and GetHashCode.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With