Faizan S.
Faizan S.

Reputation: 8644

Why is C# calling the wrong overload?

I have following code that really does funny stuff:

class Parent {
    public virtual void DoSomething(IEnumerable<string> list) {
        Console.WriteLine("Parent.DoSomething(IEnumerable<string>)");
    }
}

class Child : Parent {
    public override void DoSomething(IEnumerable<string> list) {
        Console.WriteLine("Child.DoSomething(IEnumerable<string>)");
    }

    public void DoSomething(IEnumerable<object> list) {
        Console.WriteLine("Child.DoSomething(IEnumerable<object>)");
    }
}

As you can see, the DoSomething method in Child is overridden correctly.

The output of the following code is very unexpected:

...
Child c = new Child();
var list = new List<string> { "Hello", "World!" };
c.DoSomething(list);
...

Prints Child.DoSomething(IEnumerable<object>)

Whereas assinging a Parent reference to c generates the correct output:

...
Parent c = new Child();
var list = new List<string> { "Hello", "World!" };
c.DoSomething(list);
...

Prints Child.DoSomething(IEnumerable<string>)

Why does this happen?!

Upvotes: 7

Views: 2668

Answers (3)

Shiv Kumar
Shiv Kumar

Reputation: 9799

@Lasse,

you wrote

Because the compiler only uses the type of the object that it knows about, and it only knows that the object supports what's in Parent, and thus only has one method available when resolving the method invocation.

I'm not sure if I understand what you're saying, but in the second case, since the instance is an instance of Child and the method being called is virtual, the method resolution is done at run time and not compile time, and in fact it is the child's method that is being called.

As regards method resolution rules. Shouldn't it choose a more specific method? I know it's a moot point at this stage. in 3.5 and 2 it would pick the more specific method.

Upvotes: 0

Floyd
Floyd

Reputation: 1918

posible soulution, try:

class Child : Parent {
    public override void DoSomething(IEnumerable<string> list) {
         Console.WriteLine("Child.DoSomething(IEnumerable<string>)");
    }

    public void DoSomething(IEnumerable<object> list) {
        if(list is IEnumerable<string>){
            DoSomething((IEnumerable<string>)list);
            return;
        }
        else  
            Console.WriteLine("Child.DoSomething(IEnumerable<object>)");
    }
}

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500555

It happens because the C# compiler obeys the spec :)

The specification says that if any method declared in a derived type is applicable, any methods originally declared in a base class (even if they're overridden in the derived type) are removed from the set of candidates.

Now because you're using C# 4 (presumably) there's an implicit conversion from List<string> to IEnumerable<object> so your Child.DoSomething(IEnumerable<object>) overload is applicable, and the compiler never really considers the one using IEnumerable<string>.

I have an article about overloading which goes into this and some other oddities.

I advise you not to overload across type hierarchies - it's confusing.

Upvotes: 11

Related Questions