Jerry Andrews
Jerry Andrews

Reputation: 847

C# overloading in a nested call

given the following:

class A
{
    public void Foo(object o)
    {
        Console.WriteLine("general");
    }
    public void Foo(B o)
    {
        Console.WriteLine("specific");
    }
}

class B
{
    A a = new A();
    public void CallFoo(object x)
    {
        a.Foo(x);
    }

    public static void Main()
    {
       B b = new B();
       b.CallFoo(b);
       b.a.Foo(b);
    }
}

I observe the following output:

general
specific

So naturally, I'm baffled. What's going on here?

Upvotes: 1

Views: 236

Answers (6)

ArtFriend
ArtFriend

Reputation: 66

1) b.CallFoo(b); CallFoo takes an object type as a parameter. This object is then passed to Foo and consequently Foo(object o) will get called.

2) b.a.Foo(b); Foo(B o) will get called since the object that is passed is of type B.

Upvotes: 1

vbigham
vbigham

Reputation: 191

It's all about the method signatures. b.CallFoo() takes in an Object, naturally, it calls the overload that is expecting an object. The next call is inputs a strong typed object of type B, the compiler knows that you want to use the overload that takes an object of type B.

If you want to conditionally call the overload based on the type, you would have to do that work yourself.

For example:

class B
{
    A a = new A();
    public void CallFoo(object x)
    {
        if (x.GetType() == typeof(B))
        {
            a.Foo((B)x);
        }
        else
        {
            a.Foo(x);
        }            
    }

    public static void Main()
    {
        B b = new B();
        b.CallFoo(b);
        b.a.Foo(b);
    }
}

Upvotes: 0

Jason
Jason

Reputation: 3960

That's the expected behavior. In your first call b.CallFoo(b) you are calling method B.CallFoo(object) which in turn calls the base class's Foo(object)

The second call, you are directly accessing the Foo(B) method from class A and you're also passing a var b of type B

Upvotes: 0

Goran Štuc
Goran Štuc

Reputation: 581

b.CallFoo(object x) is being passed as type of object if you don't check instanceof it will be treated as of type Object if you change the function of A.Foo to:

public void Foo(object o){
        B test = o as B;
    if(test == null){
        Console.WriteLine("general");
    }else
{
Console.WriteLine("specific");
}
}

you will get what you are expecting

Upvotes: 0

Brendan Hill
Brendan Hill

Reputation: 3752

This is absolutely the correct behavior. Overload resolution generally occurs a compile time, not run time. (unless you're doing some funky reflection or compiling lambda expressions ec)

In your code, at compile time, .NET only knows that x is an object:

public void CallFoo(object x)
{
    a.Foo(x);   // <--- Gets wired up to Foo(object o) as x is declared as an object!
}

Therefore the generic overload gets called in this case.

Conversely, in the second line, .NET knows for sure that it's a B because you declared it as such:

    b.a.Foo(b);  // <-- Gets wired up to Foo(B b) as declared as B!

Therefore the specific overload gets called in this case.

Upvotes: 1

MarcinJuraszek
MarcinJuraszek

Reputation: 125650

Every b.CallFoo will result in general being printed, because CallFoo takes object as parameter. Correct method overload is determined during the compilation, so the general is the only one compiler can choose here.

You can force the overload to be chosen during runtime by using dynamic as CallFoo parameter type:

public void CallFoo(dynamic x)
{
    a.Foo(x);
}

or you can write the logic by yourself, within B.CallFoo:

public void CallFoo(object x)
{
    if (x is B)
    {
        a.Foo((B)x);
    }
    else
    {
        a.Foo(x);
    }
}

or directly within a.Foo:

public void Foo(object o)
{
    if (o is B)
    {
        Foo((B)o);
    }
    else
    {
        Console.WriteLine("general");
    }
}

Upvotes: 5

Related Questions