When there is already a well-made class, it is a waste to rebuild a similar class from scratch. Inheritance allows you to inherit the members of the parent class and add or change only the necessary parts.
Learning Objectives
- Inherit a class using the
: basesyntax. - Call the parent constructor with
: base(...)from a child constructor. - Know the difference between
virtual/override/sealed override - Know how method hiding with the
newkeyword differs from overriding.
Key concepts
1) Inheritance with : base
class Animal
{
public string Name = "";
public void Eat() => Console.WriteLine($"{Name} eat");
}
class Dog : Animal //Inherit Animal
{
public void Bark() => Console.WriteLine($"{Name} Woof!");
}
Dog can be used as Animal, Name, and Eat(). C# only allows single inheritance (only one parent).
2) Calling base(...) in a child constructor
If the parent has a constructor with parameters, the child must call it directly.
class Animal
{
public string Name;
public Animal(string name) => Name = name;
}
class Dog : Animal
{
public Dog(string name) : base(name) { } //call parent constructor
}
3) virtual and override
A method marked by the parent as virtual can be redefined (overridden) by the child as override.
class Animal
{
public virtual void Speak() => Console.WriteLine("...");
}
class Dog : Animal
{
public override void Speak() => Console.WriteLine("Woof woof!");
}
Even if the variable type is Animal, if the actual object is Dog, Dog.Speak() is executed (=polymorphism, next section).
4) sealed override
An indication that "this method can no longer be overridden by children."
class Puppy : Dog
{
public sealed override void Speak() => Console.WriteLine("Wow!");
}
//Classes that inherit Puppy cannot override Speak.
5) hide with new keyword
Using new instead of override means "hiding the parent method." Be careful, polymorphism does not work.
class Animal { public virtual void Speak() => Console.WriteLine("Animal"); }
class Cat : Animal { public new void Speak() => Console.WriteLine("Cat"); }
Animal a = new Cat();
a.Speak(); //returns "Animal" — Cat's new method is ignored
override replaces the virtual dispatch table, but new is simply a new separate method with the same name.
6) protected
An access limiter used when you want to allow only children to access the parent's members.
class Animal { protected int HungerLevel = 0; }
class Dog : Animal { public void Feed() => HungerLevel--; }
See as example
Example 1 — AnimalDog: Simple inheritance
// Program.cs
using CodingNow.Lecture.Oop08;
var d = new Dog();
d.Name = "chocolate";
d.Eat(); //inherited from parents
d.Bark(); //git unique
// Animal.cs
namespace CodingNow.Lecture.Oop08;
internal class Animal
{
public string Name = "";
public void Eat() => Console.WriteLine($"{Name} eat");
}
// Dog.cs
internal class Dog : Animal
{
public void Bark() => Console.WriteLine($"{Name} Woof!");
}
Execution result
I eat chocolate
Chocolate puppy!
Memo: Children use the parent's Name and Eat() without having to write them separately.
Example 2 — BaseCall: child constructor + base.Method()
// Program.cs
using CodingNow.Lecture.Oop08;
var d = new Dog("chocolate", "poodle");
d.Introduce();
// Animal.cs
namespace CodingNow.Lecture.Oop08;
internal class Animal
{
public string Name;
public Animal(string name)
{
Name = name;
}
public void Introduce()
{
Console.WriteLine($"I am animal {Name}.");
}
}
// Dog.cs
internal class Dog : Animal
{
public string Breed;
public Dog(string name, string breed) : base(name) //Call parent constructor first
{
Breed = breed;
}
public new void Introduce()
{
base.Introduce(); //Call parent version first
Console.WriteLine($"The species is {Breed}.");
}
}
Execution result
I am animal chocolate.
The breed is poodle.
Memo: base.Method() is a syntax that calls the same name method of the parent class as is.
Example 3 — VirtualOverride: A starting point for polymorphism
// Program.cs
using CodingNow.Lecture.Oop08;
Animal a1 = new Animal();
Animal a2 = new Dog();
Animal a3 = new Puppy();
a1.Speak();
a2.Speak();
a3.Speak();
// Animal.cs
namespace CodingNow.Lecture.Oop08;
internal class Animal
{
public virtual void Speak() => Console.WriteLine("...");
}
// Dog.cs
internal class Dog : Animal
{
public override void Speak() => Console.WriteLine("Woof woof!");
}
// Puppy.cs
internal class Puppy : Dog
{
public sealed override void Speak() => Console.WriteLine("Wow!");
}
Execution result
...
Woof woof!
Wow!
Memo: The types of variables are all Animal, but Speak(), which matches the actual object, is called. Puppy.Speak() is sealed, so it cannot be changed further below that.
Example 4 — NewKeyword : override vs new difference
// Program.cs
using CodingNow.Lecture.Oop08;
Animal a = new Cat(); //Variable type is Animal, actual object is Cat
a.Speak(); //Because it is new, the parent version ("Animal") is called.
Cat c = new Cat();
c.Speak(); //This is called the Cat version
// Animal.cs
namespace CodingNow.Lecture.Oop08;
internal class Animal
{
public virtual void Speak() => Console.WriteLine("Animal");
}
// Cat.cs
internal class Cat : Animal
{
//not override, but new — simply define a new method with the same name.
public new void Speak() => Console.WriteLine("Cat");
}
Execution result
Animal
Cat
Note: new operates as a "new method independent of its parent method". When polymorphism is required, override is almost always the correct answer.
Common Mistakes
- There are arguments in the parent constructor, but the child does not call
: base(...)— compilation error. - Trying to use a method without
virtualin a child asoverride— compilation error. - Polymorphism does not work because
newis inadvertently used in place ofoverride— debugging is difficult. - A child tries to use member
privatedirectly — blocked. If you want to show it to your children, useprotected. - Forgetting the fact that it is single inheritance, write both parents like
class A : B, C— compile error (multiple inheritance X). Multiple interfaces are OK (Part 10).
Cleanup
- Single inheritance with
class Child : Parent. Call the parent asbase(...)/base.M(). virtual+overrideis the basic framework of polymorphism. It is possible to hide withnew, but it is recommended.- Block additional redefinition with
sealed override, and expose only to children withprotected.
Try it yourself
cd src/AnimalDog
dotnet run
cd ../BaseCall
dotnet run
cd ../VirtualOverride
dotnet run
cd ../NewKeyword
dotnet run