How do I express a void method call as the result of DynamicMetaObject.BindInvokeMember?

Viewed 4392

I'm trying to give a short example of IDynamicMetaObjectProvider for the second edition of C# in Depth, and I'm running into issues.

I want to be able to express a void call, and I'm failing. I'm sure it's possible, because if I dynamically call a void method using the reflection binder, all is fine. Here's a short but complete example:

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

class DynamicDemo : IDynamicMetaObjectProvider
{
    public DynamicMetaObject GetMetaObject(Expression expression)
    {
        return new MetaDemo(expression, this);
    }

    public void TestMethod(string name)
    {
        Console.WriteLine(name);
    }

}

class MetaDemo : DynamicMetaObject
{
    internal MetaDemo(Expression expression, DynamicDemo demo)
        : base(expression, BindingRestrictions.Empty, demo)
    {
    }

    public override DynamicMetaObject BindInvokeMember
        (InvokeMemberBinder binder, DynamicMetaObject[] args)
    {
        Expression self = this.Expression;

        Expression target = Expression.Call
            (Expression.Convert(self, typeof(DynamicDemo)),
             typeof(DynamicDemo).GetMethod("TestMethod"),
             Expression.Constant(binder.Name));

        var restrictions = BindingRestrictions.GetTypeRestriction
            (self, typeof(DynamicDemo));

        return new DynamicMetaObject(target, restrictions);
    }
}

class Test
{
    public void Foo()
    {
    }

    static void Main()
    {
        dynamic x = new Test();
        x.Foo(); // Works fine!

        x = new DynamicDemo();
        x.Foo(); // Throws
    }
}

This throws an exception:

Unhandled Exception: System.InvalidCastException: The result type 'System.Void' of the dynamic binding produced by the object with type 'DynamicDemo' for the binder 'Microsoft.CSharp.RuntimeBinder.CSharpInvokeMemberBinder' is not compatible with the result type 'System.Object' expected by the call site.

If I change the method to return object and return null, it works fine... but I don't want the result to be null, I want it to be void. That works fine for the reflection binder (see the first call in Main) but it fails for my dynamic object. I want it to work like the reflection binder - it's fine to call the method, so long as you don't try to use the result.

Have I missed a particular kind of expression I can use as the target?

4 Answers
Related