Understanding C# 3.0 Features (4) Anonymous Type
This feature provides a way to create an instance without declare the type:
var mark = new { Name = "Mark", Age = 18 };
Since the type name is unknown at this time when writing code, this is called a anonymous type.
Compilation
At compile time, the compiler will generate the following type definition automatically:
[CompilerGenerated] [DebuggerDisplay(@"\{ Name = {Name}, Age = {Age} }", Type="<Anonymous Type>")] internal sealed class <>f__AnonymousType0<<Name>j__TPar, <Age>j__TPar> { [DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <Age>j__TPar <Age>i__Field;
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private readonly <Name>j__TPar <Name>i__Field; [DebuggerHidden] public <>f__AnonymousType0(<Name>j__TPar Name, <Age>j__TPar Age) { this.<Name>i__Field = Name; this.<Age>i__Field = Age; } [DebuggerHidden] public override bool Equals(object value) { <>f__AnonymousType0<<Name>j__TPar, <Age>j__TPar> type =
value as <>f__AnonymousType0<<Name>j__TPar, <Age>j__TPar>; return (((type != null) && EqualityComparer<<Name>j__TPar>.Default.Equals(this.<Name>i__Field, type.<Name>i__Field)) && EqualityComparer<<Age>j__TPar>.Default.Equals(this.<Age>i__Field, type.<Age>i__Field)); } [DebuggerHidden] public override int GetHashCode() { int num = 0x7d068cce; num = (-1521134295 * num) + EqualityComparer<<Name>j__TPar>.Default.GetHashCode(this.<Name>i__Field); return ((-1521134295 * num) + EqualityComparer<<Age>j__TPar>.Default.GetHashCode(this.<Age>i__Field)); } [DebuggerHidden] public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("{ Name = "); builder.Append(this.<Name>i__Field); builder.Append(", Age = "); builder.Append(this.<Age>i__Field); builder.Append(" }"); return builder.ToString(); } public <Age>j__TPar Age { get { return this.<Age>i__Field; } } public <Name>j__TPar Name { get { return this.<Name>i__Field; } } }
Again, a lot of illegal identifiers are used to avoid being duplicated with the type name defined by the programmers. By replacing those identifiers with more readable words, it becomes clear:
[CompilerGenerated] internal sealed class AnonymousType0<TName, TAge> { private readonly TAge _age; private readonly TName _name; public AnonymousType0(TName name, TAge age) { this._name = name; this._age = age; } public TAge Age { get { return this._age; } } public TName Name { get { return this._name; } } }
And the code at the beginning of this post is actually compiled into:
<>f__AnonymousType0<string, int> mark = new <>f__AnonymousType0<string, int>("Mark", 18);
You can notice that the anonymous type is atomic, all the properties are read only.
If running this code:
Console.WriteLine(person.GetType().Name);
we can get the type name: <>f__AnonymousType0`2. But when we are writing code using anonymous type, its type definition is not generated by compiler yet. There is no way to know the type name, this is why “var” must be used here.
Typing
Anonymous types reuse the same one type definition if their:
- number of properties are the same
- names of properties are the same
- order of property are the same
- types of properties are the same
For example:
var mark = new { Name = "Mark", Age = 18 }; var dixin = new { Name = "Dixin", Age = 18 }; Console.WriteLine(dixin.GetType() == mark.GetType()); // Prints "True".
Equality
Since the compiler also generate the code overriding the object.Equals() (see the code snippet above), two instances of the same anonymous type are considered equal if their each property’s value are equal:
var mark1 = new { Name = "Mark", Age = 18 }; var mark2 = new { Name = "Mark", Age = 18 }; Console.WriteLine(mark1.Equals(mark2)); // Prints "True".