18

When inheriting an inherited class, the new / override behaviour is not what I would expect:

$ cat Program.cs
using System;

class A {
    public virtual void SayHi() {
        Console.WriteLine("From A");
    }
}
class B : A { 
    public new virtual void SayHi()  {
        Console.WriteLine("From B");
    }
}
class C : B { 
    public override void SayHi() {
        Console.WriteLine("From C");
    }
}

public class Program {
    public static void Main() {
        A p = new C();
        p.SayHi();
    }
}

$ ./Program.exe 
From A

As class C overrides the sayHi() method I would expect the output to be From C. Why does the B class's new modifier take precedence here? What is the use case for that? Especially as it breaks the obvious use case of having C really override A.

Note that the above code was run on Mono 2.10 running on a Debian-derived distro. But I have confirmed the same behaviour using the C# compiler in MS Visual Studio.

dotancohen
  • 30,064
  • 36
  • 138
  • 197
  • 2
    `new` from `B` is hiding the virtual overload in `C`. Change `p` to type `B` and you'll see the output from `C` – asawyer Jun 14 '12 at 18:48
  • The question is "what is the use case for this?" but the actual question asked is "why does it do this?". I'm curious what the use case for this is -- intentionally defeating inheritance in a way that's non-obvious to the consumers of your API seems incredibly dangerous. – Chuu Jun 14 '12 at 20:15

5 Answers5

21

The new modifier causes member hiding, which breaks the polymorphic relationship in your class hierarchy. The SayHi method of B is treated as distinct (not an override) from A’s (thus the choice of the word “new” as keyword). C’s method then overrides B’s, not A’s (which remains hidden).

Therefore, when you call SayHi on a C instance through an A reference, the runtime would resolve it against the A type, not the C type (within which SayHi is a “new” method inherited from B).

If, on the other hand, you were to run:

B p = new C();
p.SayHi();

…you would get the expected polymorphic result:

From C

Edit: Since you requested a use-case, here’s one. Before the introduction of generics in .NET Framework 2.0, member hiding was sometimes used as a means of altering the return types of inherited methods in derived classes (something you can't do when overriding) in order to return more specific types. For example:

class ObjectContainer
{
    private object item;

    public object Item 
    {
        get { return item; }
        set { item = value; }
    }
}

class StringContainer : ObjectContainer
{
    public new virtual string Item
    {
        get { return base.Item as string; }
        set { base.Item = value as string; }
    }
}

class QuotedStringContainer : StringContainer
{
    public override string Item
    {
        get { return "\"" + base.Item + "\""; }
    }
}

The Item property of the ObjectContainer class returns a plain object. However, in StringContainer, this inherited property is hidden to return a string instead. Thus:

ObjectContainer oc = new StringContainer();
object o  = oc.Item;   // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item;   // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;   
                       // Valid, since StringContainer.Item is now resolved

The QuotedStringContainer class overrides the Item property of StringContainer, inheriting its string return type; however, it is still hidden from the object-returning Item property of ObjectContainer. If it were not this way, there would be no way of reconciling their disparate return types…

ObjectContainer oc = new QuotedStringContainer();
object o  = oc.Item;   // Valid, since ObjectContainer.Item is resolved
string s1 = oc.Item;   // Not valid, since ObjectContainer.Item is still resolved
string s2 = ((StringContainer)oc).Item;   
                       // Valid, since QuotedStringContainer.Item is now resolved
                       // (polymorphism!)
string s3 = ((QuotedStringContainer)oc).Item;   
                       // Valid, since QuotedStringContainer.Item is now resolved
Douglas
  • 53,759
  • 13
  • 140
  • 188
  • Thank you. I understand how changing B would run the C's method, but is there a use case for breaking the polymorphic relationship between A and C? The guy programming C might have good reason to override A's method, but I fail to see the use case for preventing that, no matter if B is defined as `new` or `override`. – dotancohen Jun 15 '12 at 17:07
  • 1
    Since `C` derives from `B` which derives from `A`, there is no direct polymorphic relationship between `C` and `A`, but only an indirect one via `B`. If the `B`–`A` chain is broken, then `C` has no further claim to `A`, since `B` broke channel – Douglas Jun 15 '12 at 17:33
  • I see, that does make sense. Thank you Douglas. – dotancohen Jun 15 '12 at 18:19
  • @dotancohen: I’ve added a use-class; check whether you agree with it. – Douglas Jun 15 '12 at 23:11
7

C is overriding the shadowed version of the method (which is being shadowed in B) and not overriding the one in A.

As result, when you are using a variable of type A, the SayHi defined in A is called, as it isn't overridden in C.

Community
  • 1
  • 1
Oded
  • 489,969
  • 99
  • 883
  • 1,009
6

C overrides B's method, so when you cast it to A, you end up calling the virtual defined in A.

See ECMA 334: C# Language Specification under 17.5.3 is pretty much your example (page 294).

earlNameless
  • 2,878
  • 20
  • 25
  • Thank you for the link and the mention of page. I still don't see a use case for this behaviour, though, whereas the use case of overriding A from C regardless of B is quite obvious. – dotancohen Jun 15 '12 at 17:10
2

Because the C class is not overriding the SayHi method in the A class, it's overriding the 'new' method in B. Since your cast is to A, the compiler resolves that as being a call to A.SayHi() rather than C.SayHi()

kprobst
  • 16,165
  • 5
  • 32
  • 53
2

The last example from this msdn page explains closely to what is happening here.

Basically the new modifier causes the method in A to be hidden from C, and since it is public when C overrides it overrides the method from B(which is treated as its own distinct method).

If you set the method in B to private, C would again override the method in A.

class B : A
{
    private new void SayHi()
    {
        Console.WriteLine("From B");
    }
}

Results in: From C

NominSim
  • 8,447
  • 3
  • 28
  • 38
  • Thank you, but that example refers to private methods. – dotancohen Jun 15 '12 at 17:08
  • @dotancohen Yes, because making the method in `B` private exposes the underlying `A` method to `C` to be overridden, giving the expected result. Since you have a public `B` method, it is the one being overridden. – NominSim Jun 15 '12 at 17:30