While the implementation of the overridden ToString() and GetHashCode() methods is fairly straightforward, you might be wondering how the Equals() method has been implemented. For example, if you were to define two “anonymous cars” variables that specify the same name value pairs, would these two variables be considered equal? To see the results firsthand, update your Program type with the following new method:
static void EqualityTest()
{
// Make 2 anonymous classes with identical
name/value pairs.
var firstCar = new { Color = "Bright Pink", Make
= "Saab", CurrentSpeed = 55 };
var secondCar = new { Color = "Bright Pink", Make
= "Saab", CurrentSpeed = 55 };
// Are they considered equal when using Equals()?
if (firstCar.Equals(secondCar))
Console.WriteLine("Same anonymous object!");
else
Console.WriteLine("Not the same anonymous
object!");
// Are they considered equal when using ==?
if (firstCar == secondCar)
Console.WriteLine("Same anonymous object!");
else
Console.WriteLine("Not the same anonymous
object!");
// Are these objects the same underlying type?
if (firstCar.GetType().Name ==
secondCar.GetType().Name)
Console.WriteLine("We are both the same type!");
else
Console.WriteLine("We are different types!");
// Show all the details.
Console.WriteLine();
ReflectOverAnonymousType(firstCar);
ReflectOverAnonymousType(secondCar);
}
My car is a Bright Pink Saab.
You have a Black BMW going 90 MPH
ToString() == { Make = BMW, Color = Black, Speed = 90
}
Same anonymous object!
Not the same anonymous object!
We are both the same type!
obj is an instance of: <>f__AnonymousType0`3
Base class of <>f__AnonymousType0`3 is System.Object
obj.ToString() == { Color = Bright Pink, Make = Saab,
CurrentSpeed = 55 }
obj.GetHashCode() == -439083487
obj is an instance of: <>f__AnonymousType0`3
Base class of <>f__AnonymousType0`3 is System.Object
obj.ToString() == { Color = Bright Pink, Make = Saab,
CurrentSpeed = 55 }
obj.GetHashCode() == -439083487
When you run this test code, you will see that the first conditional test where you call Equals() returns true and, therefore, the message “Same anonymous object!” prints out to the screen. This is because the compiler-generated Equals() method uses value-based semantics when testing for equality (e.g., checking the value of each field of the objects being compared).
However, the second conditional test, which makes use of the C# equality operator (==), prints out “Not the same anonymous object!” This might seem at first glance to be a bit counterintuitive. This result is because anonymous types do not receive overloaded versions of the C# equality operators (== and !=). Given this, when you test for equality of anonymous types using the C# equality operators (rather than the Equals() method), the references, not the values maintained by the objects, are being tested for equality.
Last but not least, in the final conditional test (where you examine the underlying type name), you find that the anonymous types are instances of the same compiler generated class type (in this example, <>f AnonymousType0`3) because firstCar and secondCar have the same properties (Color, Make, and CurrentSpeed).
This illustrates an important but subtle point: the compiler will generate a new class definition only when an anonymous type contains unique names of the anonymous type. Thus, if you declare identical anonymous types (again, meaning the same names) within the same assembly, the compiler generates only a single anonymous type definition.