Why anonymous types Equals implementation compares fields?

As in the question, I'm just wondering why the language developers decided to implement Equals on anonymous types that behave like a value type. Is this not misleading?

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 logical values ​​should be false. But strings with Equals calls return different values ​​when the Person type is used, and an anonymous type is used.

+42
Aug 25 2018-12-12T00:
source share
4 answers

Anonymous type instances are immutable data values ​​without behavior or identity. It does not make sense to compare - compare them. In this context, I believe that it is wise to create comparisons of structural equality for them.

If you want to switch the comparison behavior to something custom (link comparison or case insensitive), you can use Resharper to convert an anonymous type to a named class. Resharper can also generate equality members.

There is also a very practical reason: anonymous types are conveniently used as hash keys in LINQ joins and groupings. For this reason, they require semantically correct implementations of Equals and GetHashCode .

+49
Aug 25 '12 at 16:05
source share
— -

Why you need to ask the language designers ...

But I found this in an article by Eric Lipplets on Anonymous Types of Unify Inside 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. It also gives you an implementation of Equals, GetHashCode and, most for this discussion, ToString. (*)

Where is the reason in the note:

(*) We give you Equals and GetHashCode so that you can use instances of anonymous types in LINQ queries as keys for executing joins. LINQ to Objects implements joins using a hash table for and therefore we need the right Equals and GetHashCode.

+31
Aug 25 '12 at 16:12
source share

The official answer from the C # language specification (available here ):

Equals and GetHashcode methods for anonymous types override the methods inherited from the object and are defined in terms of Equals and GetHashcode properties, so that two instances of the same anonymous type are equal if and only if all their properties are equal.

(My emphasis)

Other answers explain why this is done.

+10
Aug 25 '12 at 18:13
source share

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.

+8
Aug 25 '12 at 18:55
source share



All Articles