Anonymous types and equality
Introduction to anonymous types
Most of the applications created in .NET contain classes defined by the developer. There are two types of classes. First you have the type that only contains fields and is used to transfer data, then you have a second type that also provides functionality to the application by implementing logic in methods, events, ... .
As from the .NET 3.5 framework, Microsoft has added anonymous types. Anonymous types that are created dynamically at compile time.
With anonymous types, the developer no longer has to define the type structure. Sounds great doesn't it. Although there are some limitations. For example, anonymous types can only be used as local variables. This means that the definition of a type is only valid within a single method. It is possible to pass instances of an anonymous type between methods but then the parameter of the receiving method should be of type object which is mostly not a best practice and difficult to work with.
This limits the use of anonymous types. As for most of the .NET 3.5 language enhancements, anonymous types are implemented to be able to support Linq.
To create an anonymous type, you need to use the var keyword in combination with the new object initialization syntax (also one of the .NET 3.5 language enhancements).
Sample
In the following sample, I'll show you how to create an anonymous type as well as how an anonymous type behaves when using equality methods.
using System;
class Program
{
static void Main(string[] args)
{
// Creation of an anonymous type
var p1 = new
{
FirstName = "Geert",
LastName = "Verhoeven",
Age = 26
};
// Error:
// Property or indexer 'AnonymousType#1.Age'
// cannot be assigned to -- it is read only
// p1.Age = 27;
Console.WriteLine(p1.ToString());
var p2 = new
{
FirstName = "Geert",
LastName = "Verhoeven",
Age = 26
};
var p3 = p1;
// Comparison based on the == method.
Console.WriteLine("person1 == person2: {0}",
p1 == p2);
// Comparison based on the equals method.
Console.WriteLine("person1.Equals(person2): {0}",
p1.Equals(p2));
// Comparison based on the == method.
Console.WriteLine("person1 == person3: {0}",
p1 == p3);
}
}
There are some important things to note when looking at this output.
At first, if you look at the result of the ToString() method, you can see that the behavior is different then with normal types. By default, when not overridden, the ToString() method returns the types fullname. This means that an anonymous type has an override for the ToString() method.
Secondly, the Equals method behaves differently as well. When not overridden, the Equals method checks whether 2 objects refer to the same memory object. This means that the anonymous type for the Equals method as well.
The == operator still acts the same as with normal objects. From these results we can conclude that when using the Equals method, an anonymous types acts like a value type. When using the == operator, the anonymous type acts like a reference type.
Now how is this possible since we didn't write any code for these overrides? When opening the assembly with a tool like Reflector, we can see the structure of the generated anonymous type and see that the compiler did this for us.
internal sealed class <>f__AnonymousType0<<FirstName>j__TPar, <LastName>j__TPar, <Age>j__TPar>
{
private readonly <Age>j__TPar <Age>i__Field;
private readonly <FirstName>j__TPar <FirstName>i__Field;
private readonly <LastName>j__TPar <LastName>i__Field;
public <>f__AnonymousType0(<FirstName>j__TPar FirstName, <LastName>j__TPar LastName, <Age>j__TPar Age);
public override bool Equals(object value);
public override int GetHashCode();
public override string ToString();
public <Age>j__TPar Age { get; }
public <FirstName>j__TPar FirstName { get; }
public <LastName>j__TPar LastName { get; }
}

