views:

123

answers:

3

Suppose we have:

public class FooBase
{
    public void Write(byte value)
    {
        //something
    }

    public void Write(int value)
    {
        //something
    }
}

public class Foo : FooBase
{
    public void Write(decimal value)
    {
        //something
    }
}

than this:

        var writer = new Foo();

        writer.Write(5);         //calls Write(decimal) !!
        writer.Write((byte)6);   //calls Write(decimal) !!

will call Write(decimal) overload. Why? And how can I call Write(int) or Write(byte) ?

+6  A: 

You could call Write(byte) like this:

((FooBase)writer).Write((byte)6);

Generally, C# would prefer to use an overload defined on the type directly and cast your argument rather than use an overload defined on a parent type.

It's sort of bad style to have a method shadowing a base class method in this fashion.

mquander
Exactly. The best solution really is to just use better names to avoid ambiguity.
CaptnCraig
+9  A: 

Yes, it will do that. This is effectively my brainteaser #1. This isn't really type inference in the sense that it's normally used - it's overload resolution. That's where you need to look in the spec.

Now, the compile-time type of Writer is Foo.

When you call writer.Write, the compiler will start with type Foo and work its way up the type hiearchy until it finds a method originally declared in that type that it can legitimately call with the arguments you've given. As soon as it's found one, it doesn't go any further up the hierarchy.

Now, 5 is convertible to decimal (and so is 5 after it's been specifically cast to byte) - so Foo.Write(decimal) is an applicable function member for your method call - and that's what gets called. It doesn't even consider the FooBase.Write overloads, because it's found a match already.

So far, it's reasonable - the idea is that adding a method to the base type shouldn't change overload resolution for existing code where the child type doesn't know about it. This falls down a bit when overriding is involved. Let's change your code slightly - I'm going to remove the byte version, make Write(int) virtual and override it in Foo:

public class FooBase
{
    public virtual void Write(int value)
    {
        //something
    }
}

public class Foo : FooBase
{
    public override void Write(int value)
    {
        //something
    }

    public void Write(decimal value)
    {
        //something
    }
}

Now what will new Foo().Write(5) do? It will still call Foo.Write(decimal) - because Foo.Write(int) wasn't declared in Foo, only overridden there. If you change override to new then it will be called, because that then counts as a brand new method declaration.

I think that aspect is counterintuitive - and it's not needed for versioning, as if you're overriding a method in the child class, you clearly know it's in the base class.

The moral of the story: try not to do this. You'll end up confusing people. If you derive from a class, don't add new methods with the same name but a different signature if you can possibly help it.

Jon Skeet
I thought of your brainteaser when I read this question too...
Austin Salonen
+1 for the conclusion. Don't do this.
jeroenh
+3  A: 

This is a fairly frequently asked question. Jon's analysis is of course correct. For further reading, here's my article on the subject:

http://blogs.msdn.com/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx

Eric Lippert