Sunday 22 January 2012

Anonymous Types In C# 3.0

This post is about the Anonymous types in C#, and the object initializer syntax.

It is well known that starting from C# 3.0, you can initialize an object like this.

  1. var boy = new { Name = "Jim", Age = 2 };  
You specify the properties of an object, and at compile time, the compiler will create a new (anonymous) type, with the properties you specify, and with a constructor that can take the same number of arguments.

Now, let us examine our anonymous type in detail by using reflection. We'll examine whether the type is a class, along with the type's name. If you run this,

  1. static void Main(string[] args)  
  2.         {  
  3.   
  4.             var boy = new { Name = "Jim", Age = 2 };  
  5.   
  6.             Console.WriteLine("Name=" + boy.GetType().Name);  
  7.             Console.WriteLine("BaseType=" + boy.GetType().BaseType.Name);  
  8.             Console.WriteLine("Asm=" + boy.GetType().Assembly.FullName);  
  9.             Console.WriteLine("IsClass=" + boy.GetType().IsClass);  
  10.         }  

You'll get some output like
Name=<>f__AnonymousType0`2
BaseType=Object
Asm=AnonymousTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
IsClass=True

Needless to say, the Name will be a random name, and the Assembly's name depends on your own project name. The key point is, we just established that the type is there in the assembly - which means, as I mentioned, the compiler created a type at compile time itself. You can have a look at the IL if you are interested.

Now, let us examine the observation that the anonymous type has a constructor that takes the same number of arguments as that of properties (i.e, in this case, the number of constructor arguments will be equal to the number of properties we specified in the object initializer, of types string and int). We can do that easily by creating a new instance of the anonymous type we have, using reflection. Add this to the tail end of the last piece of code.

  1. //These two calls will fail with a run time exception, Just keep them commented  
  2. //var newboy = Activator.CreateInstance(boy.GetType(), null);  
  3. //var newboy = Activator.CreateInstance(boy.GetType(), new object[] { "Joe"});  
  4.   
  5. //Only this will work. Creating a new boy  
  6. var newboy=Activator.CreateInstance(boy.GetType(),new object[]{"Joe",2});  
  7.   
  8. //Just write back the name of new boy  
  9. Console.WriteLine(newboy.GetType().GetProperty("Name").GetValue(newobj, null));  

You can see that only the last call will work, as we need to pass the same number of arguments while instantiating.

Note that you can have arrays of anonymous types as well, like this.

  1. var guys = new[]  
  2.          {  
  3.                  new {Name="Joe", Age=10},  
  4.                  new {Name="James", Age=22},  
  5.                  new {Name="Jim", Age=23},  
  6.                  new {Name="Jerard", Age=10},  
  7.          };  

Further more, I just want to mention one more point. You may not need to assign the property explicitly, when you use the object initializer syntax. Consider this code.

  1. var guy1 = new { Name = "Ken", Age = 30 };  
  2. var guy2 = new { guy1.Name, Age = 30 };  
  3.   
  4. Console.WriteLine(guy1.GetType().Name + " - " + guy1.Name);  
  5. Console.WriteLine(guy2.GetType().Name + " - " + guy2.Name);  


The key point to note here is, for guy 2, we are not explicitly specifying the Name property. Instead, we are just specifying that the name of Guy2 is same as Guy1. The compiler will assume the property name from the given parameter's name (in fact, the last part of the specified parameter, in this case 'Name') - This is called Projection. Obviously, you'll find that both guy1 and guy2 are of the same type as well.

Can you guess the output of this code as well?

  1. string Name = "Guy2";  
  2.   
  3. var guy1 = new { Name = "Ken", Age = 30 };  
  4. var guy2 = new { Name, Age = 30 };  
  5.   
  6. Console.WriteLine(guy1.GetType().Name + " - " + guy1.Name);  
  7. Console.WriteLine(guy2.GetType().Name + " - " + guy2.Name);  


Why anonymous types are interesting? Because we use them in various ways, especially while using LINQ syntax. Here is a basic example


  1. var oldGuys = from guy in guys  
  2.                           where guy.Age > 20  
  3.                           select new { guy.Name };  

No comments :