Menu
Coddy logo textTech

Compile vs Runtime Poly

Part of the Object Oriented Programming section of Coddy's C# journey — lesson 25 of 70.

Polymorphism means "many forms" - the ability for the same code to behave differently depending on context. C# supports two distinct types of polymorphism, and understanding when each is resolved helps you write more flexible code.

Compile-time polymorphism (also called static polymorphism) is resolved by the compiler before your program runs. Method overloading is the primary example - multiple methods share the same name but have different parameters:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Compiler picks int version
calc.Add(5.0, 3.0);   // Compiler picks double version

The compiler examines the argument types and selects the correct method at compile time. This decision is fixed before the program ever executes.

Runtime polymorphism (dynamic polymorphism) is resolved while the program runs. This happens with virtual and override methods - the actual type of the object determines which method executes:

Animal pet = new Dog();  // Declared as Animal, actually a Dog
pet.Speak();             // Calls Dog's Speak() at runtime

The compiler doesn't know which Speak() will run - that decision happens at runtime based on the object's true type. This is what makes inheritance-based polymorphism so powerful for building flexible, extensible systems.

challenge icon

Challenge

Easy

Let's build a converter system that demonstrates both types of polymorphism in action. You'll create a class that uses method overloading (compile-time polymorphism) alongside an inheritance hierarchy that uses virtual/override methods (runtime polymorphism).

You'll organize your code across four files:

  • Converter.cs: Define a Converter class in the Conversion namespace. This class demonstrates compile-time polymorphism through method overloading. Create three overloaded Convert methods:
    • Convert(int value) returns "Integer: {value}"
    • Convert(double value) returns "Double: {value}"
    • Convert(string value) returns "String: {value}"
  • Shape.cs: Define a base Shape class in the Conversion namespace. This class has a Name property (string) and a constructor that sets it. Include a virtual method called Describe() that returns "This is a {Name}".
  • Circle.cs: Define a Circle class in the Conversion namespace that inherits from Shape. Add a Radius property (double). The constructor accepts a radius and passes "Circle" to the base constructor. Override Describe() to return "This is a Circle with radius {Radius}".
  • Program.cs: In your main file, demonstrate both types of polymorphism. First, create a Converter and call all three overloaded methods with input values. Then create a Circle, store it in a Shape variable, and call Describe() to show runtime polymorphism.

You will receive four inputs:

  • An integer value
  • A double value
  • A string value
  • A circle radius (double)

Print the output in this format:

Compile-time Polymorphism:
{Convert(int) result}
{Convert(double) result}
{Convert(string) result}
Runtime Polymorphism:
{Describe() result from Shape variable holding Circle}

For example, if the inputs are 42, 3.14, Hello, and 5.5, the output should be:

Compile-time Polymorphism:
Integer: 42
Double: 3.14
String: Hello
Runtime Polymorphism:
This is a Circle with radius 5.5

Notice the key difference: the compiler decides which Convert method to call based on argument types at compile time, while the Describe() method is resolved at runtime based on the actual object type, even though it's stored in a Shape variable!

Cheat sheet

Polymorphism means "many forms" - the ability for the same code to behave differently depending on context.

Compile-time polymorphism (static polymorphism) is resolved by the compiler before the program runs. Method overloading is the primary example:

public class Calculator
{
    public int Add(int a, int b) => a + b;
    public double Add(double a, double b) => a + b;
}

var calc = new Calculator();
calc.Add(5, 3);       // Compiler picks int version
calc.Add(5.0, 3.0);   // Compiler picks double version

The compiler examines argument types and selects the correct method at compile time.

Runtime polymorphism (dynamic polymorphism) is resolved while the program runs using virtual and override methods:

Animal pet = new Dog();  // Declared as Animal, actually a Dog
pet.Speak();             // Calls Dog's Speak() at runtime

The actual type of the object determines which method executes at runtime, not the declared variable type.

Try it yourself

using System;
using Conversion;

class Program
{
    public static void Main(string[] args)
    {
        // Read inputs
        int intValue = Convert.ToInt32(Console.ReadLine());
        double doubleValue = Convert.ToDouble(Console.ReadLine());
        string stringValue = Console.ReadLine();
        double radius = Convert.ToDouble(Console.ReadLine());

        // TODO: Demonstrate Compile-time Polymorphism
        // Create a Converter object and call all three overloaded Convert methods
        Console.WriteLine("Compile-time Polymorphism:");
        // Call Convert with intValue, doubleValue, and stringValue

        // TODO: Demonstrate Runtime Polymorphism
        // Create a Circle object and store it in a Shape variable
        // Then call Describe() on the Shape variable
        Console.WriteLine("Runtime Polymorphism:");
        // Create Circle and call Describe()
    }
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming