Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method resolution order

Tags:

c#

overloading

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) ?

like image 278
Tomáš Holan Avatar asked Jan 29 '10 20:01

Tomáš Holan


People also ask

What is method resolution order in Java?

The object invoked class C, not the class, while class C inherited the class A method. The order is followed in the above code is class B - > class A. This technique is known as MRO (method resolution order). Let's understand another example of multiple inheritance.

How do I check my MRO?

To get the MRO of a class, you can use either the __mro__ attribute or the mro() method. The __mro__ attribute returns a tuple, but the mro() method returns a python list. To take a more complex example that also demonstrates depth-first search, we take 6 classes.

Does order of inheritance matter in multiple inheritance in Python?

Yes, you can do multiple inheritance. please note the order of class in ExampleSimMod matters.


2 Answers

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.

like image 70
Jon Skeet Avatar answered Oct 22 '22 18:10

Jon Skeet


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.

like image 39
mqp Avatar answered Oct 22 '22 18:10

mqp