C#4: dynamic keyword

With C#4, there is a new keyword: dynamic. This keywork allow you to use an object without defining its type and to “try” to call any method. This allows you for example to call a javascript method, to do interop COM and of course to use dynamic languages which use the DLR (Dynamic Language Runtime). It also can simplify reflection.


Since C#2.0, the .NET Framework includes generic. We can define some constraints on generic types as to be a class, a struct, to inherit a class, to implement an interface, to have a no parameter public constructor. But sometimes, it isn’t efficient.


This is a basic example:


public interface I1


{


    void MyMethod();


}


 


public class C1 : I1


{


    public void MyMethod()


    {


    }


}


  


public static class MyMethodEncapsulation


{


    public static void MyMethod<T>(T item) where T : I1


    {


        item.MyMethod();


    }


}


 


In the previous code, we can do item.MyMethod() because the item type implements I1.


Now the question is how to do the same if you haven’t I1? Indeed, it isn’t possible to define a constraint “T has e method MyMethod without parameter and which returns nothing”.


With C#2 (and 3), you have too possibilities: use a delegate or use reflection.


public static void MyMethod<T>(T item, Action<T> myMethod)


{


    myMethod(item);


}


 


MyMethodEncapsulation.MyMethod(new C1(), c1 => c1.MyMethod());


The delegate is very interesting because there is compilation verification.


public static void MyMethod<T>(T item)


{


    var getMethod = typeof(T).GetMethod(“MyMethod”);


    if (getMethod == null)


        throw new InvalidOperationException();


    getMethod.Invoke(item, new object[0]);


}


 


MyMethodEncapsulation.MyMethod(new C1());


 


Using reflection is interesting because the call is easier.


 


With C#4 and dynamic, we can write this:


public static void MyMethod<T>(T item)


{


    dynamic di = item;


    di.MyMethod();


}


 


This code is shorter and easier to read.


But what is the executed code?


If we look at it with reflector, we have the following:


public static void MyMethod<T>(T item)


{


    object di = item;


    if (<MyMethod>o__SiteContainer0<T>.<>p__Site1 == null)


    {


        <MyMethod>o__SiteContainer0<T>.<>p__Site1 =


            CallSite<Action<CallSite, object>>.Create(


                new CSharpCallPayload(RuntimeBinder.GetInstance(), false, false“MyMethod”, typeof(object), null));


    }


    <MyMethod>o__SiteContainer0<T>.<>p__Site1.Target(<MyMethod>o__SiteContainer0<T>.<>p__Site1, di);


}


 


<MyMethod>o__SiteContainer0<T> is a static class (and so also <>p__Site1). So there is a cache.


The idea of CallSite is to overlap delegates to execute.


 


Now a real life example:


Philippe wanted to develop a TryGet extension method on SPBaseCollection. In fact, SPListCollection, SPUsers, SPWebs inherit SPBaseCollection and have a string indexer. The problem is the fact that the indexer isn’t defined in the base class.  


In compilation, indexer is replaced by get_Item


Philippe do this:


private static Dictionary<Type, MethodInfo> s_GetItemMethodInfos = new Dictionary<Type, MethodInfo>();


public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)


{


    result = default(T);


    try


    {


        MethodInfo methodInfo = null;


        Type spCollectionType = spBase.GetType();


        if (s_GetItemMethodInfos.ContainsKey(spCollectionType) == false)


        {


            methodInfo = spCollectionType.GetMethod(“get_Item”,


                            BindingFlags.InvokeMethod | BindingFlags.ExactBinding | BindingFlags.Instance |


                            BindingFlags.Public | BindingFlags.DeclaredOnly,


                            null,


                            new Type[] { typeof(string) },


                            null);


            s_GetItemMethodInfos.Add(spCollectionType, methodInfo);


        }


        else


            methodInfo = s_GetItemMethodInfos[spCollectionType];


 


        if (methodInfo == null)


            return false;


 


        result = (T)methodInfo.Invoke(spBase, new object[] { name });


        return true;


    }


    catch (Exception ex)


    {


        Trace.Write(String.Format(“Extension Method TryGet : {0}”, ex.InnerException.Message), “Monitorable”);


        return false;


    }


}


 


I think he should test if ex.InnerException isn’t null in the catch but it isn’t the subject here.


With C#4, we can easily do this:


public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)


{


    result = default(T);


    dynamic dynamicSpBase = spBase;


    try


    {


        result = dynamicSpBase.get_Item(name);


        return true;


    }


    catch (Exception ex)


    {


        Trace.Write(String.Format(“Extension Method TryGet : {0}”, ex.InnerException.Message), “Monitorable”);


        return false;


    }


}


 


It’s a shame that you can’t do dynamicSpBase[name] but it’s just the first CTP…


It’s important to know that if the method doesn’t exist, we will have a Microsoft.CSharp.RuntimeBinder.RuntimeBinderException.


The previous code really executed is the following:


public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)


{


    result = default(T);


    object dynamicSpBase = spBase;


    try


    {


        if (<TryGet>o__SiteContainer0<T>.<>p__Site1 == null)


        {


            <TryGet>o__SiteContainer0<T>.<>p__Site1 = CallSite<Func<CallSite, object, T>>.Create(


                new CSharpConversionPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),


                                            typeof(T),


                                            CSharpConversionPayload.ConversionKindEnum.ImplicitConversion));


        }


        if (<TryGet>o__SiteContainer0<T>.<>p__Site2 == null)


        {


            <TryGet>o__SiteContainer0<T>.<>p__Site2 = CallSite<Func<CallSite, object, string, object>>.Create(


                new CSharpCallPayload(Microsoft.CSharp.RuntimeBinder.RuntimeBinder.GetInstance(),


                                      false,


                                      false,


                                      “get_Item”,


                                      typeof(object),


                                      null));


        }


        result = <TryGet>o__SiteContainer0<T>.<>p__Site1.Target(


            <TryGet>o__SiteContainer0<T>.<>p__Site1,


            <TryGet>o__SiteContainer0<T>.<>p__Site2.Target(


                <TryGet>o__SiteContainer0<T>.<>p__Site2,


                dynamicSpBase,


                name));


        return true;


    }


    catch (Exception ex)


    {


        Trace.Write(string.Format(“Extension Method TryGet : {0}”, ex.InnerException.Message), “Monitorable”);


        return false;


    }


}


 


result is affected by the cast to T (CSharpConversionPayload) of the get_Item method call (CSharpCallPayload).


So the dynamic allows you a gain in productivity but also in the code readability (when you use it intelligently).


You will probably see later that reflection is just one of the possibilities offered by the dynamic keyword.With C#4, dynamic is usable by objects, javascript, Python, Ruby and COM.

This entry was posted in 7671, 7672. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>