3

I have noticed an unexpected behavior while trying to extend a base class. Here is a sample for this problem:

public class Program
{
    static void Main(string[] args)
    {
        var test = new CoolProgram();
        test.Start();
    }

    private void Start()
    {
        var arr = new object[]
        {
            1, // int
            1L, // long
            "Hello World" // string
        };

        foreach (var dyn in arr.Cast<dynamic>())
        {
            DoSomething(dyn);
        }

        Console.ReadKey();
    }

    protected virtual void DoSomething(int i)
    {
        Console.WriteLine("Int:" + i);
    }

    protected virtual void DoSomething(string str)
    {
        Console.WriteLine("Str:" + str);
    }
}

The Program defines two methods DoSomething which are overloaded by int and string arguments. The Start method creates an array of objectwhich contains boxed values. After the definition, the elements will be iterated with casteddynamic`. This works fine so far (without the long value).

If I create an additional inherited class CoolProgram and add another method to the type for long the program will throw an RuntimeBinderException and tells me that the best overload was DoSomething(int). The method of CoolProgram was not executed.

public class CoolProgram : Program
{
    protected override void DoSomething(int i)
    {
        // This works
        Console.WriteLine("Cool Int: " + i);
    }

    protected override void DoSomething(string str)
    {
        // This works
        Console.WriteLine("Cool Str: " + str);
    }

    protected virtual void DoSomething(long i)
    {
        // This is a new method for long
        Console.WriteLine("Long:" + i);
    }
}

Can anybody explain this behavior or have a solution for it?

1
  • 1
    dynamic just defers type resolution and subsequent compilation steps to runtime. So what you're really asking is why overload resolution does what it's doing. Commented Jan 19, 2018 at 9:21

2 Answers 2

2

Your example can be simplified further:

static void Main(string[] args)
{
    Program test = new CoolProgram();
    dynamic i = 1L;

    test.DoSomething(i);
    //test.Start();
}

The issue is that as far as the Start() method is concerned, this has the type Program. Adding a virtual overload for DoSomething() means nothing in the context of the Start() method.

Likewise in my simplified example above. The compile-time type of test is Program, just as the type of this in Start() is Program. Thus, the DoSomething(long) overload is not visible in that context, and can't be called.

If you want it to be visible, you need to either add it to the base class, or make sure that the reference used to call DoSomething() is statically typed as CoolProgram. Note that since the method is declared as protected in CoolProgram, you'd also have to change the accessibility, if you want to go the route of changing the static type of the reference to CoolProgram.

Finally, if you really want fully dynamic type resolution here, you can:

private void Start()
{
    dynamic this2 = this;

    var arr = new object[]
    {
    1, // int
    1L, // long
    "Hello World" // string
    };

    foreach (var dyn in arr.Cast<dynamic>())
    {
        this2.DoSomething(dyn);
    }

    Console.ReadKey();
}

The above would of course require that DoSomething(long) be made public, as I noted above.

Sign up to request clarification or add additional context in comments.

1 Comment

I corrected the typos. Thank you! Now I understand what is happening here.
0

1)first issue is access modifier protected due to which you are not able to access the overloaded method with Long type as a parameter. i changed it to internal now it is accessible.

2) The second issue is you are creating child class CoolProgram object but you are calling parent class DoSomething method you should use test.DoSomething(dyn); and make the child class object global to access it in your Start method.

public class Program
{
static CoolProgram test;
static void Main(string[] args)
{
    test = new CoolProgram();
    test.Start();
}

private void Start()
{
    var arr = new object[]
    {
        1, // int
        1L, // long
        "Hello World" // string
    };
    //test.DoSomething(21474836470);
    foreach (var dyn in arr.Cast<dynamic>())
    {
        test.DoSomething(dyn);
    }

    Console.ReadKey();
}

protected virtual void DoSomething(int i)
{
    Console.WriteLine("Int:" + i);
}

protected virtual void DoSomething(string str)
{
    Console.WriteLine("Str:" + str);
}
}
// from here child class
public class CoolProgram : Program
{
  protected override void DoSomething(int i)
  {
    // This works
    Console.WriteLine("Cool Int: " + i);
    base.DoSomething(i);
 }

 protected override void DoSomething(string str)
 {
    // This works
    Console.WriteLine("Cool Str: " + str);
 }

 internal virtual void DoSomething(long i)
 {
    // This is a new method for long
    Console.WriteLine("Long Int:" + i);
 }

 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.