Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing a dynamic parameter throws RuntimeBinderException when calling Method from Inherited interface

Tags:

c#

dynamic

c#-4.0

Ran into an interesting runtime issue after some refactoring and have pinned in down to the following situation.

When passing a property from a dynamic object to a method on an Interface that has been inherited from a parent interface the runtime binder cannot find the method.

Here is a test to demonstrate both failure and success (when calling method directly on the parent interface type)

using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Test.Utility
{
    public interface IEcho
    {
        string EchoString(string input);
    }

    public interface IInheritEcho : IEcho
    { }

    public class EchoClass : IInheritEcho
    {
        public string EchoString(string input)
        {
            return input;
        }
    }

    [TestClass]
    public class RuntimeBinderTest
    {
        [TestMethod]
        public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_inherited_interface()
        {
            //Arrange
            dynamic dynObject = new ExpandoObject();
            dynObject.Foo = "Bar";
            IInheritEcho echomore = new EchoClass();

            string echo = null;
            string exceptionMessage = null;

            //Act
            try
            {
                echo = echomore.EchoString(dynObject.Foo);
            }
            catch (RuntimeBinderException e)
            {
                exceptionMessage = e.Message;
            }

            //Assert
            Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage);
        }

        [TestMethod]
        public void RuntimeBinder_should_work_when_dynamic_parameters_are_passed_to_method_from_noninherited_interface()
        {
            //Arrange
            dynamic dynObject = new ExpandoObject();
            dynObject.Foo = "Bar";
            IEcho echomore = new EchoClass();

            string echo = null;
            string exceptionMessage = null;

            //Act
            try
            {
                echo = echomore.EchoString(dynObject.Foo);
            }
            catch (RuntimeBinderException e)
            {
                exceptionMessage = e.Message;
            }

            //Assert
            Assert.AreEqual(echo, dynObject.Foo, false, exceptionMessage);
        }
    }
}

Test #1 Fails: Assert.AreEqual failed. Expected:<(null)>. Actual:. 'Test.Utility.IInheritEcho' does not contain a definition for 'EchoString'

Test #2 Succeeds.

My question is whether my assumption that the 1st test should pass is correct or is there a fundamental reason in the framework that it does not?

I know I can fix the issue by casting the parameters when I pass them in or assigning them to variables before passing them in. I'm more just curious as to the reason the inherited interface is causing the RuntimeBinder to fail...

like image 728
piff Avatar asked May 10 '12 09:05

piff


2 Answers

You situations is a documented bug on Microsoft Connect

like image 91
igofed Avatar answered Oct 21 '22 15:10

igofed


A good question this.

It would appear that it's taking the type of the expression at compile time, IInheritEcho, and not deep-searching the members of inherited interfaces when looking for the method to invoke dynamically.

And ideally the C# runtime binder for a dynamic expression should behave the same way as the C# compiler - therefore it should see that the IEcho interface is inherited by IInheritEcho and it should work.

We can test the hypothesis - i.e. that it's the static typing - by doing this in the first test:

echo = ((dynamic)echomore).EchoString(dynObject.Foo);

Hey presto - the test passes.

So the issue is not that the dynamic binder can't find the method - it's that when the instance whose member is being invoked dynamically is statically typed as an interface, inherited interfaces are not consulted.

In my opinion this behaviour is incorrect.

Please Eric Lippert... be nice...

like image 2
Andras Zoltan Avatar answered Oct 21 '22 17:10

Andras Zoltan