The following program prints
A:C(A,B)
B:C(A,B)
(as it should)
public interface I
{
string A();
}
public class C : I
{
public string A()
{
return "A";
}
public string B()
{
return "B";
}
}
public class A
{
public virtual void Print(C c)
{
Console.WriteLine("A:C(" + c.A() + "," + c.B() + ")");
}
}
public class B : A
{
public new void Print(C c)
{
Console.WriteLine("B:C(" + c.A() + "," + c.B() + ")");
}
public void Print(I i)
{
Console.WriteLine("B:I(" + i.A() + ")");
}
}
class Program
{
public static void Main(string[] args)
{
A a = new A();
B b = new B();
C c = new C();
a.Print(c);
b.Print(c);
}
}
however, if I change keyword 'new' to 'override' in class B like so:
public override void Print(C c)
all of a sudden program starts to print:
A:C(A,B)
B:I(A)
Why?
This is to do with how overloaded methods are resolved.
Effectively (simplified somewhat), the compiler first looks at the declared type of the expression (B) in this case and looks for candidate methods which are first declared in that type. If there are any methods which are appropriate (i.e. where all the arguments can be converted to the method's parameter types) then it doesn't look at any parent types. This means that overridden methods, where the initial declaration is in a parent type, don't get a look-in if there are any "freshly declared" appropriate methods in the derived type.
Here's a slightly simpler example:
using System;
class Base
{
public virtual void Foo(int x)
{
Console.WriteLine("Base.Foo(int)");
}
}
class Derived : Base
{
public override void Foo(int x)
{
Console.WriteLine("Derived.Foo(int)");
}
public void Foo(double d)
{
Console.WriteLine("Derived.Foo(double)");
}
}
class Test
{
static void Main()
{
Derived d = new Derived();
d.Foo(10);
}
}
This prints Derived.Foo(double)
- even though the compiler knows there is a matching method with a parameter of type int
, and the argument is type int
, and the conversion from int
to int
is "better" than the conversion from int
to double
, the fact that only the Foo(double)
method is originally declared in Derived
means the compiler ignores Foo(int)
.
This is highly surprising IMO. I can see why it would be the case if Derived
didn't override Foo
- otherwise introducing a new, more specific, method in the base class could change the behaviour unexpectedly - but clearly Derived
here knows about Base.Foo(int)
as it's overriding it. This is one of the (relatively few) points where I believe the C# designers made the wrong decision.