Broadcast Babble

Musings on Code, Technology, and Television

Matthew

Doig

Why VB.Net Needs a Dynamic Keyword

I had just written a couple of blog posts about using the dynamic keyword with Json.Net when a coworker asked me about doing the same in Vb.Net. I told him that vb.net does not have a dynamic keyword as you’ve always been able to do runtime member binding, just turn option strict and option infer off. Something like this should work just fine.

 

Option Infer Off
Option Strict Off
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Dynamic

Module Module1

Sub Main()
    Dim user As String = "{'name':'Matthew Doig','link':'http://www.facebook.com/mattdoig'}"
    Dim userJson = JObject.Parse(user)
    Console.WriteLine(userJson.name)
    Console.Read()
End Sub

End Module

 

Except it doesn’t work just fine, instead a MissingMemberException is thrown with the message: “public ‘member’ name on type ‘JObject’ not found”. Perusing the internet and the consensus seems to be to use the array syntax.

 

Option Infer Off
Option Strict Off
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Imports System.Dynamic

Module Module1

Sub Main()
    Dim user As String = "{'name':'Matthew Doig','link':'http://www.facebook.com/mattdoig'}"
    Dim userJson = JObject.Parse(user)
    Console.WriteLine(userJson("name"))
    Console.Read()
End Sub

End Module

 

And this does work, but it doesn’t answer the question why the dot syntax doesn’t. Runtime member binding is a feature VB.net has had since long before the dynamic keyword in csharp. So why doesn’t it work for Json.Net?

 

We take a quick look at the IL in reflector to see if there’s any difference between the c# and VB.Net programs. And lo and behold, vb generates the following call when making the late binding call.

 

RuntimeHelpers.GetObjectValue(
    NewerLateBinding.LateGet(
        myObject, null, "name", new object[0], null, null, null))

 

And if we make the same call from c# then we get the same exception message.

 

using System;
using System.Runtime.CompilerServices;
using Microsoft.VisualBasic.CompilerServices;
using Newtonsoft.Json.Linq;

class Program
{
    static void Main(string[] args)
    {
        var user = "{\"name\":\"Matthew Doig\"," +
                    "\"link\":\"http://www.facebook.com/mattdoig\"}";
        dynamic userJson = JObject.Parse(user);
        Console.WriteLine(userJson.name);

        Console.WriteLine(RuntimeHelpers.GetObjectValue(
            NewLateBinding.LateGet(
                userJson, null, "name", new object[0], null, null, null)));
            
        Console.Read();
    }
}

 

So something in the LateGet method is causing our late bound call to a JObject to fail at runtime.

 

Looking through the source code of Json.Net and we find that the JObject overrides GetMetaObject to return an object responsible for the runtime binding operations on the object. This post details an object that uses the same methodology for runtime binding. The code simply logs when binding operations are called at runtime.

 

using System;
using System.Dynamic;
using System.Linq.Expressions;

public class MyDynamicObject : IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression parameter)
    {
        return new MyMetaObject(parameter, this);
    }

    public class MyMetaObject : DynamicMetaObject
    {
        public MyMetaObject(Expression parameter, object value)
            : base(parameter, BindingRestrictions.Empty, value)
        {
        }

        public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args)
        {
            return this.PrintAndReturnIdentity("InvokeMember of method {0}", binder.Name);
        }

        public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
        {
            return this.PrintAndReturnIdentity("SetMember of property {0}", binder.Name);
        }

        public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
        {
            return this.PrintAndReturnIdentity("GetMember of property {0}", binder.Name);
        }

        private DynamicMetaObject PrintAndReturnIdentity(string message, string name)
        {
            Console.WriteLine(String.Format(message, name));
            return new DynamicMetaObject(
                Expression,
                BindingRestrictions.GetTypeRestriction(
                    Expression,
                    typeof(MyDynamicObject)));
        }
    }
}

 

And when we run the following code we see something rather interesting.

 

using System;
using System.Dynamic;
using System.Runtime.CompilerServices;
using DynamicObject;
using Microsoft.VisualBasic.CompilerServices;

class Program
{
    static void Main(string[] args)
    {
        dynamic myObject = new MyDynamicObject();
        myObject.name = "Matthew Doig";
        Console.WriteLine(myObject.name);

        Console.WriteLine(RuntimeHelpers.GetObjectValue(
            NewLateBinding.LateGet(myObject, null, "name", 
                                   new object[0], null, null, null)));

        Console.Read();
    }
}

 

SetMember of property name
GetMember of property name
DynamicObject.MyDynamicObject
InvokeMember of method name
DynamicObject.MyDynamicObject

 

The myObject.name call results in a “GetMember of property name” while LateGet results in an “InvokeMember of method name”. So now it makes sense why late binding is not working for VB.Net.

 

Option Infer Off
Option Strict Off
Imports DynamicObject

Module Module1

    Sub Main()
        Dim myObject = New MyDynamicObject()
        myObject.name = "Matthew Doig"
        Console.WriteLine(myObject.name)
        Console.Read()
    End Sub

End Module

 

 

SetMember of property name
InvokeMember of method name
MyDynamicObject

 

The above code results in a call to BindInvokeMember instead of BindGetMember, and of course there is no method called “name” on the object. Because you can call methods without a () in VB, VB has no way to know whether its supposed to call BindInvokeMember or BindGetMember at runtime.

 

This is where a dynamic keyword would come in handy. The dynamic keyword would ensure you get the same behavior as csharp. Basically dynamic would tell the compiler not to emit a call to LateGet and simply let the dlr take care of the runtime binding.

2 Responses to “Why VB.Net Needs a Dynamic Keyword”

  1. Thanks this helped in finding a solution, which was to use JSonFX to parse to dynamic instead ;-)

    http://blogs.lessthandot.com/index.php/DesktopDev/MSTech/json-net-s-jobject-to

    Comment by chrissie1 — November 11, 2012 @ 8:57 am

  2. [...] took a look at how Json.Net works with the C# dynamic keyword and how Vb.net is unable to take advantage of the nicer runtime syntax provided by the dynamic keyword because of its legacy support for runtime bindings. Today, in the [...]

    Pingback by Json and the F# Type Provider — November 26, 2012 @ 9:27 am

RSS feed for comments on this post. TrackBack URL

Leave a Response