🌊 Polymorphism: Many Forms, One Name
Polymorphism means "many forms." It allows objects of different classes to be treated through the same base type, yet each responds in its own way. You call the same method on different objects, and each one behaves according to its actual type at runtime.
Picture an earthquake hitting a city. The same seismic force hits every building — but a wood house flexes, a steel tower absorbs shock, and a brick building cracks. Same event, different responses. That's polymorphism.
virtual and override — Runtime Polymorphism
Mark a base method as virtual to allow derived classes to override it. When you call the method through a base-type reference, C# looks up the actual type at runtime and calls the overridden version. This is called runtime (dynamic) polymorphism.
class Building
{
public string Name;
public Building(string name) { Name = name; }
// virtual — can be overridden by derived classes
public virtual string Withstand()
{
return "Standing...";
}
}
class WoodHouse : Building
{
public WoodHouse(string name) : base(name) { }
public override string Withstand()
{
return "Flexing structure... Survived!";
}
}
class SteelTower : Building
{
public SteelTower(string name) : base(name) { }
public override string Withstand()
{
return "Absorbing shock... Survived!";
}
}
// Polymorphism in action:
Building b = new WoodHouse("Cabin"); // base-type reference
Console.WriteLine(b.Withstand()); // Calls WoodHouse.Withstand()!
Method Hiding with new
If a derived class declares a method with the same name as a base method without using override, it hides the base method. The compiler warns you — adding the new keyword makes the hiding explicit. Unlike override, hiding does not participate in polymorphism: the method called depends on the reference type, not the object type.
class Base
{
public virtual void Greet() => Console.WriteLine("Hello from Base");
}
class DerivedOverride : Base
{
public override void Greet() => Console.WriteLine("Hello from Override");
}
class DerivedNew : Base
{
public new void Greet() => Console.WriteLine("Hello from New");
}
Base a = new DerivedOverride();
a.Greet(); // "Hello from Override" — polymorphism works!
Base b = new DerivedNew();
b.Greet(); // "Hello from Base" — hiding! Reference type wins
The is and as Operators — Type Checking at Runtime
Use is to check if an object is of a certain type. Use as to attempt a safe cast — it returns null instead of throwing an exception if the cast fails.
Building building = new WoodHouse("Pine Lodge");
// is — returns true/false
if (building is WoodHouse)
Console.WriteLine("It's a wood house!");
// is with pattern matching (C# 7+)
if (building is WoodHouse wood)
Console.WriteLine($"Wood house: {wood.Name}");
// as — safe cast, returns null on failure
SteelTower tower = building as SteelTower;
if (tower == null)
Console.WriteLine("Not a steel tower.");
🏗️ Why Polymorphism Matters
Without polymorphism, you'd need endless if/else chains checking the type of each object. With it, you store different building types in a single Building[] array and call the same method — the correct version runs automatically. This is the foundation of extensible, maintainable software.
📋 Instructions
**Your Mission: Simulate an Earthquake Across the City!**
Create a base `Building` class and three derived classes that respond differently to an earthquake.
1. Create a `Building` class:
- `public string Name` field
- Constructor takes a `string name`
- A `virtual` method `public virtual string Withstand()` that returns `"Standing..."`
2. Create `WoodHouse : Building`:
- Constructor calls `base(name)`
- Override `Withstand()` to return `"Flexing structure... Survived!"`
3. Create `SteelTower : Building`:
- Constructor calls `base(name)`
- Override `Withstand()` to return `"Absorbing shock... Survived!"`
4. Create `BrickBuilding : Building`:
- Constructor calls `base(name)`
- Override `Withstand()` to return `"Cracking detected... Damage!"`
5. In `Main`:
- Print `"Earthquake simulation:"`
- Create a `Building[]` array with: `new WoodHouse("Wood House")`, `new SteelTower("Steel Tower")`, `new BrickBuilding("Brick Building")`
- Loop through the array and print `"{Name}: {Withstand()}"` for each
- Print `"Polymorphism in action!"`
Store all buildings in a `Building[]` array. When you call `building.Withstand()` through the base-type array, C# automatically calls the overridden version for each actual type. That's the power of `virtual` + `override`.