Skip to content

Intermediate · Inheritance, interfaces and records · topic 19 of 43 · 30 min

Abstract classes and polymorphism

Explanation

Polymorphism means one piece of code can work with many kinds of objects: a variable of the base type can hold any derived object, and calling a virtual method runs the version of the actual object. An abstract class is a base class that cannot be created on its own and can declare abstract methods that every derived class must implement.

The idea of polymorphism

Polymorphism (Greek for "many forms") means you can write code against a general type and let each specific object do its own thing. A checkout page should not need a separate branch for UPI, cards and net banking. It should say "payment, process yourself", and each payment type knows how.

Think of it like this: A school principal says "Everyone, introduce yourselves" in the assembly. She does not give separate instructions to each student. Each student hears the same request and answers in their own way. That single request with many different answers is polymorphism.

abstract classes

Sometimes the base class is only an idea. There is no such thing as a plain "Payment"; there is always a UPI payment or a card payment. Mark such a class abstract. You cannot create it with new, but you can use it as a variable type. An abstract method has no body; every non-abstract derived class must override it.

csharp
public abstract class Payment
{
    public decimal Amount { get; }
    protected Payment(decimal amount) => Amount = amount;

    public abstract decimal Fee();                      // no body: each type decides
    public decimal Total() => Amount + Fee();            // normal method, shared
    public override string ToString() => $"{GetType().Name}: {Amount} + fee {Fee()} = {Total()}";
}

public class UpiPayment : Payment
{
    public UpiPayment(decimal amount) : base(amount) { }
    public override decimal Fee() => 0m;
}

public class CardPayment : Payment
{
    public CardPayment(decimal amount) : base(amount) { }
    public override decimal Fee() => Math.Round(Amount * 0.02m, 2);
}

public class NetBankingPayment : Payment
{
    public NetBankingPayment(decimal amount) : base(amount) { }
    public override decimal Fee() => 10m;
}
An abstract class can still have fields, constructors and normal methods. Total() works for every type because it calls the abstract Fee().

Polymorphism in action

csharp
Payment[] payments =
{
    new UpiPayment(500m),
    new CardPayment(1000m),
    new NetBankingPayment(750m)
};

foreach (Payment p in payments)
    Console.WriteLine(p);

// var x = new Payment(10m);   // error CS0144: cannot create an instance of the abstract type
One loop, three behaviours. Shows UpiPayment: 500 + fee 0 = 500, CardPayment: 1000 + fee 20.00 = 1020.00, NetBankingPayment: 750 + fee 10 = 760.

The decision of which Fee() to run is made at run time, by looking at the real object. This is called dynamic dispatch or late binding. The loop does not change when you add a new WalletPayment class tomorrow, which is the real benefit: new features without editing old, tested code.

Overriding versus hiding versus overloading

TermWhat it isDecided
OverloadingSame name, different parameters, in one classAt compile time, by argument types
OverridingDerived class replaces a virtual/abstract methodAt run time, by the object's real type
Hiding (new)Derived class declares a member with the same name, no overrideAt compile time, by the variable's type

Checking and converting types

csharp
Payment p = new CardPayment(200m);
if (p is CardPayment card)
    Console.WriteLine($"Card fee: {card.Fee()}");

var maybe = p as UpiPayment;     // null if p is not a UpiPayment
Console.WriteLine(maybe is null);  // True
is with a name both checks and converts. as gives null instead of throwing. A direct cast (UpiPayment)p would throw InvalidCastException here.
Tip: If you find yourself writing many if (x is A) ... else if (x is B) ... checks, it often means the behaviour should be a virtual method on the base class instead.

Common mistakes and fixes

  • Not implementing an abstract method: error CS0534, the derived class does not implement the inherited abstract member. Add the override.
  • Trying new on an abstract class: error CS0144. Create a concrete derived class.
  • Calling an abstract or virtual method from the base constructor: the derived object is not fully set up yet, so it may see default values. Avoid it.
  • Making a class abstract with no abstract members just to block new: a protected constructor or simply a normal class may be clearer.

Need help?

Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.

Try:

Watch the lesson

Abstract classes and polymorphism · 1/7

Polymorphism

In this lesson you will write code that works with many kinds of objects at once.

Prefer a video by a teacher? Find free videos on “C# polymorphism abstract class abstract method explained” on YouTube ↗

In real life: Calculating delivery charges for different courier partners

An Indian e-commerce seller ships through several courier partners. Each partner prices differently: by weight, by zone, or a flat rate. The order screen works with the abstract Courier type, so adding a new partner means adding one class.

csharp
Courier[] partners = { new FlatCourier(), new WeightCourier() };
foreach (var c in partners)
    Console.WriteLine($"{c.Name}: Rs {c.Charge(2.5)}");

public abstract class Courier
{
    public abstract string Name { get; }
    public abstract decimal Charge(double kg);
}

public class FlatCourier : Courier
{
    public override string Name => "QuickShip";
    public override decimal Charge(double kg) => 60m;
}

public class WeightCourier : Courier
{
    public override string Name => "KiloExpress";
    public override decimal Charge(double kg) => 30m + 20m * (decimal)Math.Ceiling(kg);
}
Shows QuickShip: Rs 60 and KiloExpress: Rs 90. Properties can be abstract too. The partner names are made up.

Practice (0/4 done)

Task 1 · Question

Can you write new Shape() if Shape is abstract?

Task 2 · Question

What does this print? Shape[] s = { new Square(2), new Circle(1) }; foreach (var x in s) Console.Write(x.Name() + " "); abstract class Shape { public abstract string Name(); } class Square : Shape { int a; public Square(int a) => this.a = a; public override string Name() => "Square"; } class Circle : Shape { int r; public Circle(int r) => this.r = r; public override string Name() => "Circle"; }

Task 3 · Question

object o = "hello"; What is o as string?

Task 4 · Practice

Create an abstract class Notification with an abstract method Send(string to, string text) returning a string, and three classes: SmsNotification, EmailNotification and WhatsAppNotification, each returning text like "SMS to 98xxxx: Your OTP is 4321". Store them in a List<Notification> and send the same OTP through each.

Quick quiz

Question 1

1.What is polymorphism?

Question 2

2.What must a non-abstract class do with an inherited abstract method?

Question 3

3.When is the overridden method chosen?

Question 4

4.Can an abstract class have a constructor and normal methods?

Question 5

5.Payment p = new UpiPayment(10m); var c = (CardPayment)p; What happens?

Read more (free)

Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.

What is polymorphism?

Answer: One piece of code working with many types, each responding in its own way

Calls through a base type run each object's own override.

What must a non-abstract class do with an inherited abstract method?

Answer: Override it

Otherwise error CS0534.

When is the overridden method chosen?

Answer: At run time by the object's actual type

Virtual dispatch happens at run time.

Can an abstract class have a constructor and normal methods?

Answer: Yes

Abstract classes can contain state, constructors and implemented methods.

Payment p = new UpiPayment(10m); var c = (CardPayment)p; What happens?

Answer: InvalidCastException at run time

A direct cast to the wrong type throws; use is or as to check safely.

A base constructor calls a virtual function. Which version runs?Related

Answer: The base version, because the derived part is not built yet

During base construction the object behaves as a base object.

From Virtual functions and polymorphism

When is the overridden method to run chosen?Related

Answer: At run time, based on the actual object

That is dynamic dispatch.

From Overriding and polymorphism

Can you declare a variable of an abstract class type?Related

Answer: Yes, and assign it a subclass object

Payment p = new UpiPayment(…) is polymorphism.

From Abstract classes

Can an abstract class have a constructor and data members?Related

Answer: Yes; it just cannot be instantiated on its own

Abstract only means "cannot be created directly". It may still hold shared data and constructors for derived classes.

From Abstract classes, interfaces and good class design

Pass the quiz (60% or more) or mark this topic as done when you are ready.

Your progress is saved on this device only.

Next steps

  1. 2 more topics in “Inheritance, interfaces and records” before its exam: Interfaces: contracts for behaviour, Records and pattern matching.
  2. Module 5 exam: Inheritance, interfaces and records · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
  3. Then the next module, Collections, generics and LINQ. 43 topics left in the course before the final exam.

← Previous: Inheritance: base and derived classes