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) ?
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.
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.
Yes, you can do multiple inheritance. please note the order of class in ExampleSimMod matters.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With