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.
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
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