Source: Ch6_OOP.pdf
Method Hiding and Polymorphism
Compile-time vs runtime polymorphism, overloading, overriding, method hiding with new, and upcasting.
Compile-time Polymorphism — Overloading
Overloading means multiple methods share a name but have different parameter lists. The compiler resolves which version to call at compile time, so it is called static (compile-time) polymorphism.
class Display
{
public void Show(int n) => Console.WriteLine("int: " + n);
public void Show(double d) => Console.WriteLine("double: " + d);
public void Show(string s) => Console.WriteLine("string: " + s);
}
Display d = new Display();
d.Show(5);
d.Show(3.14);
d.Show("Hello");Output
int: 5 double: 3.14 string: Hello
Overloading — Exercise 1
The compiler picks the best matching overload based on argument types. Ambiguous calls that match two overloads equally cause a compile error.
Add the missing overload to handle a double argument
Hint
Add a Format(double d) overload to handle the double argument.
At what stage is an overloaded method call resolved?
Runtime Polymorphism — Overriding
When a base-type variable holds a derived object and you call a virtual method, C# calls the derived override at runtime. This is dynamic dispatch.
class Animal
{
public virtual string Sound() => "...";
}
class Dog : Animal
{
public override string Sound() => "Woof";
}
class Cat : Animal
{
public override string Sound() => "Meow";
}
Animal[] animals = { new Dog(), new Cat(), new Dog() };
foreach (Animal a in animals)
Console.WriteLine(a.Sound());Output
Woof Meow Woof
Overriding — Exercise 1
Runtime polymorphism requires both virtual on the base and override on the derived. Missing either defeats dynamic dispatch.
Fix the missing virtual and override keywords
Hint
Add virtual to Printer.Print() and override to ColorPrinter.Print().
What type of polymorphism uses virtual and override?
Method Hiding with new
The new keyword intentionally hides a base class member with a same-name derived member. Unlike override, there is no runtime dispatch — the declared type of the variable determines which version runs.
class A
{
public virtual void F() => Console.WriteLine("A.F");
}
class B : A
{
public new void F() => Console.WriteLine("B.F (hidden)");
}
B b = new B();
b.F(); // B.F
A a = new B();
a.F(); // A.F — no polymorphism with newOutput
B.F (hidden) A.F
Method Hiding — Exercise 1
When a derived method uses new instead of override, calling through a base-type reference always invokes the base version.
Change hiding to override for true polymorphism
Hint
Replace new with override so that Logger reference uses FileLogger's version at runtime.
What does new do in a derived member declaration?
Upcasting and Downcasting
Upcasting (derived → base) is implicit and safe. Downcasting (base → derived) requires an explicit cast and throws InvalidCastException if the object is the wrong type. Use is and as for safe checks.
class Animal { public virtual void Speak() => Console.WriteLine("..."); }
class Dog : Animal { public override void Speak() => Console.WriteLine("Woof"); }
class Cat : Animal { public override void Speak() => Console.WriteLine("Meow"); }
Animal a = new Dog(); // upcast (implicit)
a.Speak(); // Woof
if (a is Dog dog) // safe downcast with pattern matching
Console.WriteLine("It's a dog!");
Animal b = new Cat();
Dog? d = b as Dog; // returns null if wrong type
Console.WriteLine(d == null ? "Not a dog" : "Dog");Output
Woof It's a dog! Not a dog
Upcasting / Downcasting — Exercise 1
A direct explicit cast to the wrong type throws InvalidCastException. Use as or is to cast safely.
Fix the unsafe downcast with as
Hint
Use as to attempt the cast — it returns null instead of throwing on failure.
What does the as operator return when the cast fails?