Weird C# dynamic behaviour [duplicate]
Asked Answered
S

1

7

While investigating how C# dynamic keyword works, I stumbled upon some weird behaviour. It almost looks like a bug, but it probably more likely there is a reason for the behaviour.

In the code below, there are two calls, one to obj1 and one to obj2, but only one of them executes correctly. It seems like the local variable type is the reason, but "Hello" should also be accessible from IDynamicTarget, because it extends IDynamicTargetBase.

namespace DynamicTesting
{
    interface IDynamicTargetBase
    {
        string Hello(int a);
    }

    interface IDynamicTarget : IDynamicTargetBase
    {
    }

    class DynamicTarget : IDynamicTarget
    {
        public string Hello(int a)
        {
            return "Hello!";
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            dynamic a = 123;

            IDynamicTargetBase obj1 = new DynamicTarget();
            obj1.Hello(a);  // This works just fine

            IDynamicTarget obj2 = new DynamicTarget();
            obj2.Hello(a); // RuntimeBinderException "No overload for method 'Hello' takes '1' arguments"
        }
    }
}
Surge answered 25/1, 2015 at 10:0 Comment(3)
Seems to work on Mono: ideone.com/PGn3Jp . Here's a version that fails on .Net Fiddle: dotnetfiddle.net/55ZMAGMcnair
This is one hell of a bug to be ignoring it for five years now..Tonyatonye
BTW is a low priority bug because if argument is compile-time typed, the same code in this question will work. I feel that there're few cases where you give a dynamic argument...Sneaky
M
0

It seems like it's a method overload resolution issue.

Just change dynamic a = 123 to int a = 123 and your code will work. Also, if you change the method call to obj2.Hello((int)a);. Finally, type the variable as DynamicTarget instead of IDynamicTarget and it will work too!

Why? When you work with dynamic expressions and there're more than a method overload which invokation has dynamic arguments, runtime won't be able to resolve which overload to call, because method overload resolution is based on the type and order of the arguments provided when the so-called method was called.

My speculation is runtime overload resolution fails when an interface also implements other interface, and the run-time seems to understand that there's no guarantee that the second interface will define an overload of one of other interfaces that also implements, and it forces you to provide the actual type of the argument(s) during compile-time.

[...] but "Hello" should also be accesible from IDynamicTarget, because it extends IDynamicTargetBase.

It's accessible, but the runtime is unable to resolve how to provide method's arguments...

Mesnalty answered 25/1, 2015 at 11:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.