C# 4.0 dynamic keyword for Dummies – Under the hood


ANOOP MADHUSUDANAN

Vote on HN

In this post, we’ll go back to the basics to uncover few interesting aspects of C# 4.0 dynamic features. Let us start with a simple experiment.

Static Typing or Early Binding

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

image

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.

image

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 type

image

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

image

By the way, why we call dynamic typing as ‘Duck typing’?

image

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.

image

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’.

image

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.

image

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).

image

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.

image

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.

image 

Conclusion

A good follow up read is my post on ExpandoObject, which explains how to define which operations can be performed on dynamic objects and how to perform those operations. For example, you can define what happens when you try to get or set an object property, call a method, or perform standard mathematical operations such as addition and multiplication.

Also, A more interesting and useful dynamic concept I’ve recently implemented is ElasticObject, a fluent DynamicObject implementation, to deal with data formats like XML in a very easy manner.

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.CompilerServices;
using Microsoft.CSharp.RuntimeBinder;

namespace DynamicTest
{
    class Human
    {
        public void Walk(dynamic place)
        {
            Console.WriteLine("I'm walking to " + place);
        }
        public dynamic Age { get; set; }
    }

    class Program
    {
          // Methods
    private static void Main(string[] args)
    {
        //dynamic h = new Human();
        //h.Walk("Paris");
        //h.Age = 10;

        //will get compiled to

        object h = new Human();

        //Create the site 1 if it is null
        if (SiteContainer0.Site1 == null)
        {
            SiteContainer0.Site1 = CallSite<Action<CallSite, object, string>>.Create
                (Binder.InvokeMember(CSharpBinderFlags.ResultDiscarded, "Walk", 
                null, typeof(Program), new CSharpArgumentInfo[] 
                { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
                  CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.LiteralConstant 
                  | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
        }
        SiteContainer0.Site1.Target(SiteContainer0.Site1, h, "Paris");

        //Create the site 2 if it is null
        if (SiteContainer0.Site2 == null)
        {
            SiteContainer0.Site2 = CallSite<Func<CallSite, object, int, object>>.Create
                (Binder.SetMember(CSharpBinderFlags.None, "Age", typeof(Program), 
                new CSharpArgumentInfo[] 
                { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), 
                    CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.LiteralConstant | 
                    CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
        }
        SiteContainer0.Site2.Target(SiteContainer0.Site2, h, 10);
    }
    

    // Nested Types
    [CompilerGenerated]
    private static class SiteContainer0
    {
        // Fields
        public static CallSite<Action<CallSite, object, string>> Site1;
        public static CallSite<Func<CallSite, object, int, object>> Site2;
    }

    }

}

Enjoy, Happy Coding!! Do keep in touch, follow me In Twitter @amazedsaint Or Or Subscribe this Blog

Shout it
© 2012. All Rights Reserved. Amazedsaint.com