In the previous post, we discussed using the var keyword. One of the primary use cases of the var keyword is anonymous types. In this post, we are going to look closer into anonymous types.

Anonymous types

An anonymous type is a nameless class that inherits from an object.

The type is inferred, by the compiler, at initialization.

For example, a typical anonymous declaration would look like this:

var person = new 
{
  FirstName = "John",
  LastName = "Power",
  Age = 33
};

We can see, just like any other object, it supports IntelliSense. We can see all the properties we defined and the methods coming from the Object class, such as ToString() and Equals(). So it is a strongly-typed class. We don’t know the type.

Auto-complete showing the properties of person object

The properties are read-only. If we try to assign another value, we get the following error:

Compilation error (line 11, col 3): Property or indexer 'AnonymousType#1.Age' cannot be assigned to -- it is read only
IDE showing error when assigning value to property

Since we don’t have a handle on the class, how can we create another object of this type? The answer is simple: We make another anonymous type with the same properties.

For example, the following example would compile successfully:

public class Program
{
    public static void Main()
    {
        var firstPerson = new 
        {
          FirstName = "John",
          LastName = "Power",
          Age = 33
        };

        var secondPerson = new 
        {
          FirstName = "Jane",
          LastName = "Power",
          Age = 44
        };

        firstPerson = secondPerson;
    }
}

secondPerson can be assigned to firstPerson as they have the same type. An assignment is only possible if all the properties match. If we remove the Age property from the secondPerson, we get the error shown below:

Compilation error (line 18, col 17): Cannot implicitly convert type 'AnonymousType#1' to 'AnonymousType#2'

Shorthand Declarations

We don’t need to specify the property names if we assign the object from another. So, for example, if we wanted to create a second object of the same type with the same values, we could use this syntax:

Auto-complete showing the properties on secondPerson object

As shown in the screenshot above, we can still see the same property names as the firstPerson object.

The same feature exists in ES6.

Internals of Anonymous Types

So what happens when we compile our application with anonymous types? The type names are generated automatically by the compiler.

The example below shows what our class looks like with an IL viewer:

The output of IL viewer showing the compiler output of anonymous types

We declared the firstPerson and secondPerson objects defined (of the same type) as:

instance void class '<>f__AnonymousType0`3'<string, string, int32>::.ctor(!0/*string*/, !1/*string*/, !2/*int32*/)

These auto-generated type names are hidden from the developer because we don’t need to know what they are. So it’s generally a bad practice to find out these types via reflection.

The final example shows the IL output when I’ve removed the Age property from the secondPerson object. Now that the properties don’t match with firstPerson, the compiler generates a new type for secondPerson named <> f__AnonymousType1’2:

The output of IL viewer showing the compiler output of anonymous types when Age property is removed from secondPerson object

Resources


Volkan Paksoy

Volkan Paksoy is a software developer with more than 15 years of experience, focusing mostly on C# and AWS. He’s a home lab and self-hosting fan who loves to spend his personal time developing hobby projects with Raspberry Pi, Arduino, LEGO and everything in-between.