The world of .NET from a Connected Systems MVP & INETA Speaker

C# 4.0/BCL 4 Series:Dynamic Primitive Type Part 2

This is part of a series. Last time I started talking about the new dynamic type in C# 4, I covered why they were introduced to the language (scenarios), then some deep discussion of how they are implemented with the DLR, call-sites, and bindings, how internally they are represented as System.Object and the equivalence relationships as well as implicit and explicit conversions. This time, I am going to go deeper into the bindings and show some code. Speaking of scenarios, one is to remove/replace Reflection code. I just found Rick Strahl's excellent post on replacing mounds of Reflection code with just a little dynamic code.

First a little more review and generalities. Don't forget the concept that dynamic is a type in the C# type system. It has special meaning but it's definetly a type, and it's important to treat it as such. You can indicate dynamic as the the type as the type of the variable you declare, the type of items in a collection or the return value of a method. You can also use dynamic as the type of a method parameter.

Also, to review...

A dynamic type is declared with the keyword dynamic:

      dynamic d = GetSomeObject();

      d.Quack();

A dynamic type tells the complier to relax. We expect the runtime type of d to have a Quack method but we just can't prove it statically. Since d is dynamic, the compiler defers binding Quack to d until runtime. When your code invokes a member using a dynamic expression/variable, the compiler generates special IL code describing the desired operation, known as the payload. So, instead of emitting an IL instruction to Quack(), which the compiler cannot clearly do since there is no type with that definition, the compiler emits a "dynamic call site" that manages that operation at runtime, using the C# runtime Binder, and in conjunction with the DLR. At runtime, the payload code determines the exact operation to invoke based on the actual type of the object now referenced by the dynamic expression/variable. The payload code uses a class known as a runtime binder. The code for the C# runtime binder is in the Microsoft.CSharp assembly and yoo must reference this assembly when you build projects that use the dynamic keyword.

There are actually four kinds of Binding:

  1. Using reflection against an underlying CLR type
  2. Invoking a custom IDynamicMetaObjectProvider, which makes available a DynamicMetaObject
  3. Calling through IUnknown and IDispatch interfaces of COM
  4. Calling type defined by dynamic languages such as IronPython.

What's the order? At runtime, the C# language binder resolves a dynamic operation according to the runtime type of the object. The binder first checks to see if the type implements the IDynamicMetaObjectProvider interface. If it does, then the interface's GetMetaObject method is called, which returns a DynamicMetaObject-derived type. This type can process all the member, method, and operator bindings for the object. IDynamicMetaObjectProvider and DynamicMetaObject both are defined in the System.Dynamic namespace, and both are in the System.Core.dll assembly.

Dynamic languages, like Ruby and Python, endow their types with DynamicMetaObject-derived types so that they can be accesses in a way appoporiate for them when manipulated from other programming languages (like C#). Simarly, when accessing a COM component, the C# runtime binder will use a DynamicMetaObject-derived type that knows how to communicate with a COM component.

If the type of the object being used in the dynamic expression does NOT implement the IDynamicMetaObjectProvider interface, then the C# compiler treats the object like an instance of an ordinary C# defined type and performs operations using reflection.

"Custom Binding"

Custom Binding occurs when a dynamic object implements IDynamicMetaObjectProvider (IDMOP). Although you can implement IDMOP on types that you write in C#, and that is useful to do, the more common case is that you have acquired an IDMOP object from a dynamic language that is implemented in .NET on the DLR such as IronPython or IronRuby. Objects from these languages implictly implement IDMOP as a means by which to control the meanings of operations performed on them. Here is a simple one that the two authors of  C# 4.0 In A Nutshell wrote on Page 162-163:

using System;
using System.Dynamic;
 
namespace IDMOPCustomBinding
{
    class Program
    {
        static void Main(string[] args)
        {
            dynamic d = new Duck();
            d.Quack();              // Quack method was called
            d.Waddle();             // Waddle method was called
        }
    }
 
    public class Duck : DynamicObject
    {
        public override bool TryInvokeMember(InvokeMemberBinder binder, 
                object[] args, out object result)
        {
            Console.WriteLine(binder.Name + " method was called");
            result = null;
            return true;
        }
    }
}

The Duck class doesn't actually have a Quack method. Instead, it uses custom binding to intercept and interpret all method calls.

"Language Binding"

"Language binding" occurs when a dynamic object does not implement IDynamicMetaObjectProvider. Language binding is useful when working around imperfectly designed types or inherent limitations in the .NET type system. A typical problem when using numeric types is that they have no common interface. You can bind operators *dynamically* just as you bind methods:

using System;
 
namespace DynamucTypesLanguageBinding
{
    class Program
    {
        static dynamic Mean(dynamic x, dynamic y)
        {
            return (x + y)/2;
        }
 
        static void Main(string[] args)
        {
            int x = 3, y = 4;
            Console.WriteLine(Mean(x,y));
        }
    }
}
 
 

Running this will display 3. The benefit is obvious - you don't have to duplicate code for each numeric type. However, this (unneccessarily) sacrifices static type safety. The following compiles without error, but then fails at runtime:

     string s = Mean(3, 5);      // Runtime error!

We can fix this by introducing a generic type parameter, and then casting to dynamic within the calculation itself:

 static T Mean<T> (T x, T y)
        {
            dynamic result = ((dynamic) x + y)/2;
            return (T) result;
        }

COM Interop

Here is an example of some C# code that uses COM IDispatch to create a Microsoft Office Excel workbook and places a string in cell A1. Note: I verified this with the real Excel 2010 that is now available from TechNet.

using System;
using Microsoft.Office.Interop.Excel;
 
namespace DynamicCOMInterop
{
    class Program
    {
        static void Main(string[] args)
        {
            Application excel = new Application();
            excel.Visible = true;
            excel.Workbooks.Add(Type.Missing);
            ((Range) excel.Cells[1, 1]).Value = "Text in cell A1";
        }
    }
}
 

This following explanation is from Jeff Richter's "CLR via C#": "Without the dynamic type, the value returned from excel.Cells[1, 1] is of type Object, which must be cast to the Range type before its value property is accessed. However, when producing a runtime callable wrapper assembly for a COM object, any use of VARIANT in the COM method is really converted to dynamic; this is called dynamification. Therefore, since excel.Cells[1,1[ is of type dynamic, you do not have to explicitly cast it to the Range type before its Value property can be accessed. Dynamification can greatly simplify code that interoperates with COM objects. Here is the simpler code:"

using System;
using Microsoft.Office.Interop.Excel;
 
namespace DynamicCOMInterop
{
    class Program
    {
        static void Main(string[] args)
        {
            Application excel = new Application();
            excel.Visible = true;
            excel.Workbooks.Add(Type.Missing);
            excel.Cells[1, 1].Value = "Text in cell A1";
        }
    }
}
 

Phew! That was a lot of stuff for this post. I hope it helps.

» Similar Posts

  1. C# 4.0/BCL 4 Series:Dynamic Primitive Type Part 1
  2. SOA: Making the Paradigm Shift Part 7 of N
  3. SOA: Making the Paradigm Shift Part 11 of N

» Trackbacks & Pingbacks

  1. Pingback from Markus Tamm » Blog Archive » Links 03.06.2010

Trackback link for this post:
http://samgentile.com/Web/trackback.ashx?id=1913

» Comments

    There are no comments. Kick things off by filling out the form below.

» Leave a Comment