Because it gives us something useful. Consider the following:
var countSameName = from p in PersonInfoStore group p.Id by new {p.FirstName, p.SecondName} into grp select new{grp.Key.FirstName, grp.Key.SecondName, grp.Count()};
It works because the implementation of Equals() and GetHashCode() for anonymous types works on the basis of a peer field.
- This means that the above will be closer to the same query when starting with
PersonInfoStore , which is not an object binding. (Still, not the same thing, it will correspond to what the XML source will do, but not what will lead to the failure of most databases). - This means that we don’t need to define an
IEqualityComparer for each GroupBy call, which would make the group a very difficult task with anonymous objects - perhaps, but not easy to define an IEqualityComparer for anonymous objects - and are far from the most natural meaning. - First of all, this does not cause problems with most cases.
The third question is worth exploring.
When we determine the type of value, we naturally want a concept of equality based on value. Although we may have a different idea about value-based equality than the default value, for example, when matching a given field case-insensitively, the default value is natural (if it does not work well and does not work in one case *). (In addition, in this case referential equality is meaningless).
When we define a reference type, we may or may not want the concept of equality based on value. The default value gives us reference equality, but we can easily change that. If we change it, we can change it only for Equals and GetHashCode or for them, as well as == .
When we define an anonymous type, oh wait, we have not defined it, which means anonymous! Most of the scenarios in which we care about referential equality no longer exist. If we are going to keep the object around long enough to think later if it will be the same as the other, we probably are not dealing with an anonymous object. Often there are times when we care about equality based on value. Very often with Linq ( GroupBy , as we saw above, but also Distinct , Union , GroupJoin , Intersect , SequenceEqual , ToDictionary and ToLookup ) and often with other uses (it's not like we did not do what Linq does for us with enumerations in 2.0 and to some extent to this, any encoding in 2.0 could write half of the methods in Enumerable themselves).
In general, we get a lot from how equality works with anonymous classes.
In that someone really wants referential equality, == using reference equality means that they still have this, so we are not losing anything. This is the way.
* The default implementation of Equals() and GetHashCode() by default has an optimization that allows you to use binary matching in cases where it is safe. Unfortunately, there is a mistake due to which it is sometimes erroneous to identify some cases as safe for this faster approach, when they are absent (or at least it was used, it may have been fixed). The usual case is if there is a decimal field in the structure, then it will consider some instances with equivalent fields as unequal.