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...
You situations is a documented bug on Microsoft Connect
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With