Inheritance and interfaces
A base class for what several values share, a derived class for what makes one special, and an interface as a promise one list can hold.
A class that is a special case
When two kinds of value share most of their parts, the shared parts go in a base class and each special case goes in its own derived class. The colon writes the relationship: class Square : Shape says a square is a kind of shape. The derived class starts with everything the base class has and adds to it.
A base constructor is called with base(...) from the derived one: public Square(decimal side) : base("square"). Running the base first means the shared parts are already set before the derived class touches anything, which is why C# insists on it.
virtual, override and abstract
A method marked virtual in the base class may be replaced by a derived one, and override in the derived class does the replacing. The call site does not change: shape.Describe() runs the square version when the value is a square, because the program looks at the actual value rather than at the declared type.
abstract is the stricter form. An abstract member has no body at all, and a class that has one cannot be created directly — it is a shape of shapes, and every derived class has to supply the missing piece. That is how a base class insists on a decision instead of guessing one.
An interface is a promise
An interface lists members with no bodies: interface INamed { string Describe(); }. It says nothing about how the work is done, only that whoever claims the interface has that method. A class takes it on by listing it after the colon, next to its base class if it has one.
The payoff is a list that holds different kinds of value, as long as each one keeps the same promise: INamed[] items = { new Shape("blob"), new Square(3m) };. The loop that follows knows only that each item can describe itself, so adding a third kind tomorrow does not change the loop at all.
A base class, a promise, and two kinds of shape
using System;
// A promise: whoever implements this has Describe.
interface INamed
{
string Describe();
}
// A shape of shapes. Abstract: not created directly.
abstract class Shape : INamed
{
public string Label { get; protected set; }
protected Shape(string label) { Label = label; }
// No body here: every derived class must supply one.
public abstract decimal Area { get; }
// virtual: replaceable, but usable as it stands.
public virtual string Describe()
{
return Label + " area " + Area.ToString("F2");
}
}
class Square : Shape
{
public decimal Side { get; private set; }
public Square(decimal w) : base("square") { Side = w; }
public override decimal Area => Side * Side;
}
class Program
{
static void Main()
{
INamed[] items = { new Square(3m), new Square(2m) };
foreach (var item in items)
{
Console.WriteLine(item.Describe());
}
}
}One list, two kinds of shape
Run it and read the two lines. Then add a class for a rectangle, with a height of its own, put one in the array, and let the same loop describe it.
Prefer an interface when two classes only share a promise, and a base class when they share the work as well. Inheritance is a strong tie; interfaces are the cheap one.
The call is decided by the object
When a virtual method is called through a base-typed variable, the runtime looks at the object the variable actually points at and runs the override for that type. This is dynamic dispatch, and it is the mechanism behind the promise that the call site does not change.
The declared type decides what you are allowed to call. The actual type decides which implementation runs. Shape shape = new Square(3m); may call Describe, but it may not call a member that only Square has, because the compiler only knows the declared type.
The opposite of override is new, which hides the base method instead of replacing it. A new method with the same name is a different method that happens to share a name; a call through a base-typed variable still runs the base version, because nothing was overridden. The compiler warns about the hiding, and the warning is worth reading: most of the time override was meant.
Inheritance is a chain
A derived class can itself be a base for another class, and an override can call the version above it with base.Describe(). That lets a special case add to the shared behaviour instead of replacing it entirely.
An interface sits beside that chain rather than inside it. A class can implement several interfaces but inherit only one base class, which is why interfaces are the cheaper way to share a promise: they add a capability without deciding what the class already is.
An abstract class can implement an interface and leave some members for a derived class to finish. That combination is common: the interface says what callers may rely on, and the abstract base supplies the parts that are already known.
Three inheritance mistakes: writing new instead of override, which hides the base method and leaves the old one running through a base-typed variable; listing an interface but not implementing every member, so the class does not compile; and calling a virtual method from a constructor, before the derived object has finished setting itself up.
Try these next
- Add a
Triangleclass with its own area, put it in the sameINamedarray, and let the existing loop describe it. - Call
base.Describe()from a derived override and add one extra phrase to the result. - Make an interface with two members, implement it on one class, and call both through an interface-typed variable.
One loop, three speakers
Run it and read the three lines. Then add a third kind of speaker with its own word, put one in the array, and confirm the loop needs no change.
Check what you learned
One question at a time. A wrong answer explains itself — take another go.
Part 1 of 5
What does the colon mean in class Square : Shape?
