Get down!

We saw before how inheriting from DynamicObject allows us to quickly write an object which gets a chance to hande operations on the object, like method invocations and property access. We didn’t need to do anything with expression trees using this approach, but in order to get more of an idea of what is happening, in this post we’ll delve into the world of IDynamicMetaObjectProvider and get to the point where we can start playing with expression trees.

We’re going to define a dynamic object of type MyDynamicObject which we’ll exercise using the following harness.

static void Main(string[] args)
{
    foreach (dynamic target in new dynamic[] { new MyDynamicObject(), new MyDynamicObject() })
    {
        foreach (object targetArgument in new object[] {
            1, 2, 3, "string1", 4, 5, "string2", 1.2, "string3", "string4", target, target })
        {
            target.DoIt(targetArgument);
        }
    }
}

The skeleton of our implementation is going to be

class MyDynamicObject : IDynamicMetaObjectProvider
{

    public object OnInt(int argument)
    {
        Console.WriteLine("On int {0}", argument);
        return argument;
    }

    public object OnSomethingElse(object argument)
    {
        Console.WriteLine("On something else {0}", argument);
        return argument;
    }

We’ll implement an object that redirects any method invocation so that the method OnInt is called if the method’s first argument is an int, and onSomethingElse if the first argument is of another type.

We implemented IDynamicMetaObjectProvider which requires us to implement the method GetMetaObject. We’ll define this as follows.

public DynamicMetaObject GetMetaObject(Expression expression)
{
    Console.WriteLine("Provide meta object");
    return new MyDynamicMetaObject(expression, this);
}

The incoming expression is something that will evaluate to the target object. We pass this though to the nested dynamic metaobject subclass which we define as follows.

class MyDynamicMetaObject : DynamicMetaObject
{
    public MyDynamicMetaObject(Expression parameter, MyDynamicObject self)
        : base(parameter, BindingRestrictions.Empty, self)
    {
    }

The next method is the one that handles the code generation. We check the type of the first of the incoming arguments. If it is int then we are going to generate a call which uses the OnInt method, otherwise we’ll direct to the OnSomethingElse method. In the DynamicMetaObject that we return, we provide a restriction which demands that the argument is of the relevant type. The DLR will use this restriction to determine if we get a hit in the inline cache for the next call through the call site.

    public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        Console.WriteLine("BindInvokeMember on {0}", binder.Name);

        if (args[0].LimitType == typeof(int))
        {
            return new DynamicMetaObject(
                Expression.Call(
                  Expression.Convert(Expression, typeof(MyDynamicObject)),
                  typeof(MyDynamicObject).GetMethod("OnInt"),
                  Expression.Convert(args[0].Expression, typeof(int))),
                  BindingRestrictions.GetTypeRestriction(args[0].Expression, typeof(int))
                );
        }

        Type targetType = args[0].LimitType;

        DynamicMetaObject result =
            new DynamicMetaObject(
                Expression.Call(
                  Expression.Convert(Expression, typeof(MyDynamicObject)),
                  typeof(MyDynamicObject).GetMethod("OnSomethingElse"),
                  Expression.Convert(args[0].Expression, typeof(object))),
                  BindingRestrictions.GetTypeRestriction(args[0].Expression, targetType)
                );

        return result;
    }
}

Running this gives the following output. From the output we can see the inline caching that the DLR is doing to avoid needing to call to our metaobject if the restrictions we provide when we return an expression tree are valid. So, for example, the DLR calls our metaobject the first time the incoming first argument is an integer, and then doesn’t need to call again into the metaobject until the argument type changes, on the call with “string1”. When we call again on an int, the metaobject isn’t needed as the cache already has the code to execute for the int case. 

Provide meta object
BindInvokeMember on DoIt
On int 1
On int 2
On int 3
Provide meta object
BindInvokeMember on DoIt
On something else string1
On int 4
On int 5
On something else string2
Provide meta object
BindInvokeMember on DoIt
On something else 1.2
On something else string3
On something else string4
Provide meta object
Provide meta object
BindInvokeMember on DoIt
On something else ConsoleApplication1.MyDynamicObject
On something else ConsoleApplication1.MyDynamicObject
On int 1
On int 2
On int 3
On something else string1
On int 4
On int 5
On something else string2
On something else 1.2
On something else string3
On something else string4
On something else ConsoleApplication1.MyDynamicObject
On something else ConsoleApplication1.MyDynamicObject

Advertisements
This entry was posted in Computers and Internet. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s