Sunday, 22 January 2012

C# 4.0 dynamic keyword for Dummies – Under the hood

  1. C# 4.0 dynamic keyword
  2. C# Dynamic Binding – Language Binding

Static Typing or Early Binding

Let us start with a bare minimum program. Fire up VS2010 and try the following code.

It is obvious that this won’t compile. Simply because, the compile/design time checking ensures type safety - and as we don’t have a method named SomeStupidCall in our Human class, we can’t compile the same.


So, that is what you get.

Duck Typing or Dynamic Typing or Late Binding
Now, let us take a step back, and modify the above code like this. Change the type of variable h to ‘dynamic’. And Compile. Here is a little surprise!! You got it compiled!!  By using ‘dynamic’ keyword, you just told the compiler, “dude, don’t bother if SomeStupidCall() is there in the Human 


And now, run the application. The application will break for sure. But hey, that is your fault, not the compiler’s.

By the way, why we call dynamic typing as ‘Duck typing’?
Here we go, Quoted from Wikipedia
In duck typing, one is concerned with just those aspects of an object that are used, rather than with the type of the object itself. For example, in a non-duck-typed language, one can create a function that takes an object of type Duck and calls that object's walk and quack methods. In a duck-typed language, the equivalent function would take an object of any type and call that object's walk and quack methods. If the object does not have the methods that are called then the function signals a run-time error. It is this action of any object having the correct walk and quack methods being accepted by the function that evokes the quotation and hence the name of this form of typing.
Now, why you need ‘dynamic’ at all?
I hope the above scenario is ‘good’ enough to give you an ‘evil’ impression about the dynamic features. But, wait. There are lot of scenarios where the dynamic features can really simplify things for you. For example, let us assume a reflection based scenario, where you load a type (from an external assembly or so), to invoke a member. Here is a quick example. You’ll see how to use dynamic as an easier alternative for reflection. This is much more evident when you deal with scenarios like COM interop etc. We’ll see more interesting uses later.


How a ‘dynamic’ type is getting compiled?

Let us get back to a basic example. Consider a human class, with a Walk() method and Age property. Now, let us create a new Human and assign this to a dynamic variable ‘h’.


Let us build the above application. Fire up Reflector, open the binary executable of the app, and have a look at the same. With a bit of cleanup, this is the relevant part of the disassembled code - which is the equivalent 'statically’ typed code generated by the compiler, for the ‘dynamic’ code we wrote above.
You’ll see a ‘SiteContainer’, for keeping track of the context, and two call site fields.
And here is the disassembled code for the member invocations. It might be interesting to note that, the variable ‘h’ is compiled as a simple CLR ‘object’.

Wow, from the CLR point of few, there is nothing like a ‘dynamic’ type. Instead, all member invocations to our ‘dynamic’ object are modified; in such a way that they are piped through a dynamic Call Site. The Call Site is initialized with the Binder responsible for run time binding. In this case, you may note that the C# run time binder will be used to invoke the members(Microsoft.CSharp.RuntimeBinder.Binder ‘s InvokeMember will return a CallSite binder, that’ll be used by the Call Site).


You might be thinking how the code will be generated for scenarios where, you have a method that accepts or returns a ‘dynamic’ type, or a property that gets or sets a dynamic type. Let us try this out. Change our above Human class in the above example, to something like this. Note that now Walk method accepts a dynamic type, and Age property is also modified to get/set dynamic types.


Have a sneak peak using Reflector. You’ll note that, the compiler has generated a special [Dynamic] attribute to the input parameter of Walk() method. Also, the getter and setter of the Age property is also decorated with the [Dynamic] attribute, as shown below.


And here is some code of the final form of our experiment. Create a Console app in C# 4.0, to play with this.

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Runtime.CompilerServices;  
  6. using Microsoft.CSharp.RuntimeBinder;  
  8. namespace DynamicTest  
  9. {  
  10.     class Human  
  11.     {  
  12.         public void Walk(dynamic place)  
  13.         {  
  14.             Console.WriteLine("I'm walking to " + place);  
  15.         }  
  16.         public dynamic Age { getset; }  
  17.     }  
  19.     class Program  
  20.     {  
  21.           // Methods  
  22.     private static void Main(string[] args)  
  23.     {  
  24.         //dynamic h = new Human();  
  25.         //h.Walk("Paris");  
  26.         //h.Age = 10;  
  28.         //will get compiled to  
  30.         object h = new Human();  
  32.         //Create the site 1 if it is null  
  33.         if (SiteContainer0.Site1 == null)  
  34.         {  
  35.             SiteContainer0.Site1 = CallSite<Action<CallSite, objectstring>>.Create  
  36.                 (Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Walk",   
  37.                 nulltypeof(Program), new CSharpArgumentInfo[]   
  38.                 { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),   
  39.                   CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.LiteralConstant   
  40.                   | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));  
  41.         }  
  42.         SiteContainer0.Site1.Target(SiteContainer0.Site1, h, "Paris");  
  44.         //Create the site 2 if it is null  
  45.         if (SiteContainer0.Site2 == null)  
  46.         {  
  47.             SiteContainer0.Site2 = CallSite<Func<CallSite, objectintobject>>.Create  
  48.                 (Binder.SetMember(CSharpBinderFlags.None, "Age"typeof(Program),   
  49.                 new CSharpArgumentInfo[]   
  50.                 { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null),   
  51.                     CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.LiteralConstant |   
  52.                     CSharpArgumentInfoFlags.UseCompileTimeType, null) }));  
  53.         }  
  54.         SiteContainer0.Site2.Target(SiteContainer0.Site2, h, 10);  
  55.     }  
  58.     // Nested Types  
  59.     [CompilerGenerated]  
  60.     private static class SiteContainer0  
  61.     {  
  62.         // Fields  
  63.         public static CallSite<Action<CallSite, objectstring>> Site1;  
  64.         public static CallSite<Func<CallSite, objectintobject>> Site2;  
  65.     }  
  67.     }  
  69. }  


    C# Dynamic Binding – Language Binding

    Language binding is a form of dynamic binding which occurs when a dynamic object does not implement IDynamicMetaObjectProvider.
  71.   Language binding comes in handy when working around imperfectly designed types or the inherent limitations in the .NET type system. A common problem is that when using numeric types is they have no common interface. Using dynamic binding, methods can be bound dynamically and the same also applies for operators:
    static dynamic Mean (dynamic xObj, dynamic yObj)
    return (xObj + yObj) / 2;
    static void Main()
    int xObj = 3, yObj = 4;
    Console.WriteLine (Mean (xObj, yObj));

    The benefit here is clear — you can avoid duplicating code for each numeric type. However, in doing so you will lose static type safety, risking runtime exceptions as opposed to getting compile time errors.

  72. Note that dynamic binding only circumvents static type safety, runtime type safety is still perserved. In contrast when using reflection, you cannot circumvent member accessibility rules using dynamic binding.
    Language runtime binding behaves very similarly to static binding when the runtime types of the dynamic objects are known at compile time. In the above example, the behavior of the program would be identical if Mean was hardcoded to work with the int type.

No comments :