CACS408 Dot Net Technology

Dot Net TechnologyUnit 1010 min read

Dynamic Binding & Polymorphism in C: Virtual Methods, Overloading, Generics

Unit 10 of Dot Net Technology covers dynamic binding (runtime polymorphism via virtual/override), polymorphism (compile-time vs. runtime), operator overloading, and generics in C. Learn how to design flexible, reusable code using interfaces, abstract classes, and type-safe generics with real-world examples from Nepales

TAKEAWAYS:

  • Dynamic binding uses virtual/override to resolve method calls at runtime, enabling runtime polymorphism (e.g., eSewa processes different payment methods dynamically).
  • Operator overloading lets you redefine operators (+, -, ==) for custom classes (e.g., Complex numbers in financial calculations).
  • Generics provide type-safe, reusable collections (e.g., List<T> in Khalti’s transaction logs).
  • Compile-time polymorphism (method overloading) resolves method calls during compilation (e.g., Area() for Square/Circle in NTC’s network design tools).
  • Abstract classes and interfaces enforce contracts (e.g., IPaymentGateway in Daraz’s checkout system).
  • Polymorphism reduces code duplication by treating derived objects uniformly (e.g., NEPSE’s stock ticker updates all IStock implementations).

1. Dynamic Binding and Runtime Polymorphism

Dynamic binding occurs when the method to invoke is determined at runtime (not compile-time). C# achieves this using:

  • virtual keyword: Marks a method as overrideable in derived classes.
  • override keyword: Provides a new implementation in a derived class.
  • Base class reference to derived object: Enables polymorphic behavior.

How It Works

classDiagram
    class Animal {
        <<abstract>>
        +virtual void MakeSound()
    }
    class Dog {
        +override void MakeSound()
    }
    class Cat {
        +override void MakeSound()
    }
    Animal --> Dog : "is-a"
    Animal --> Cat : "is-a"

Example: eSewa uses dynamic binding to process payments via IPaymentMethod:

public interface IPaymentMethod {
    void ProcessPayment(double amount);
}

public class KhaltiPayment : IPaymentMethod {
    public override void ProcessPayment(double amount) {
        Console.WriteLine($"Processing Khalti payment: ${amount}");
    }
}

public class EBankingPayment : IPaymentMethod {
    public override void ProcessPayment(double amount) {
        Console.WriteLine($"Processing E-Banking payment: ${amount}");
    }
}

Runtime Behavior:

IPaymentMethod payment = new KhaltiPayment(); // Dynamic binding!
payment.ProcessPayment(5000); // Output: "Processing Khalti payment: $5000"
payment = new EBankingPayment();
payment.ProcessPayment(3000); // Output: "Processing E-Banking payment: $3000"

Key Points

  • Virtual methods allow derived classes to redefine behavior.
  • Upcasting (treating a derived object as its base type) enables polymorphism.
  • Performance cost: Dynamic binding is slower than static binding due to runtime lookup.

2. Operator Overloading

Operator overloading lets you redefine operators (+, -, ==, etc.) for custom classes. Useful for:

  • Mathematical operations (e.g., Complex numbers).
  • Collections (e.g., List<T> concatenation).

Rules for Overloading

  1. Must be a binary or unary operator.
  2. At least one operand must be of the custom class.
  3. Cannot overload:
    • &&, ||, ??, sizeof, typeof, new, is, as.

Example: Complex Number Addition

public class Complex {
    public double Real { get; set; }
    public double Imaginary { get; set; }

    // Overload the '+' operator
    public static Complex operator +(Complex a, Complex b) {
        return new Complex {
            Real = a.Real + b.Real,
            Imaginary = a.Imaginary + b.Imaginary
        };
    }
}

Trace:

Step Operation Result (a + b)
1 a = new Complex(3, 4) Real=3, Imaginary=4
2 b = new Complex(1, 2) Real=1, Imaginary=2
3 c = a + b Real=4, Imaginary=6

Real-World Use: Ncell’s billing system uses operator overloading to add data usage (DataPlan + ExtraData).


3. Generics in C#

Generics allow type-safe, reusable code by defining classes/methods with placeholder types (T). Benefits:

  • Compile-time type checking (no casting).
  • Performance (no boxing/unboxing for value types).
  • Code reuse (e.g., List<T>, Dictionary<TKey, TValue>).

Example: Generic Max Method

public static T Max<T>(T a, T b) where T : IComparable<T> {
    return a.CompareTo(b) > 0 ? a : b;
}

Trace:

Input (a, b) Type T Output (Max)
5, 3 int 5
"apple", "banana" string "banana"

Real-World Use: Pathao’s ride dispatcher uses PriorityQueue<T> to assign drivers based on location proximity.


4. Compile-Time Polymorphism (Method Overloading)

Method overloading resolves method calls at compile-time based on:

  • Parameter count.
  • Parameter types.
  • Parameter order (not return type).

Example: Area Calculator

public class AreaCalculator {
    public double Area(double radius) => Math.PI * radius * radius; // Circle
    public double Area(double length, double width) => length * width; // Rectangle
}

Trace:

Method Call Parameters Output
Area(5) radius=5 78.54
Area(4, 6) length=4, width=6 24

Real-World Use: NTC’s network design tool uses overloaded Area() to calculate cable lengths for different shapes.


5. Abstract Classes vs. Interfaces

Feature Abstract Class Interface
Instantiation Cannot be instantiated. Cannot be instantiated.
Methods Can have implemented methods. Only method signatures (no body).
Access Modifiers Supports private, protected. All members are public.
Inheritance Supports single inheritance. Supports multiple inheritance.
Fields Can have fields (even static). No fields (only properties).

Example: IShape Interface

public interface IShape {
    double Area();
}

public abstract class Shape : IShape {
    public abstract double Area();
}

public class Circle : Shape {
    public override double Area() => Math.PI * Radius * Radius;
}

Real-World Use: NEPSE’s stock ticker implements IStock to update prices uniformly.


6. Polymorphism in Action: Payment Processing

Scenario: eSewa processes payments via IPaymentMethod.

flowchart TD
    A["PaymentGateway"] --> B["IPaymentMethod"]
    B --> C["KhaltiPayment"]
    B --> D["EBankingPayment"]
    B --> E["MobilePayment"]
    A -->|"Process"| B

Code:

public class PaymentGateway {
    public void Process(IPaymentMethod method, double amount) {
        method.ProcessPayment(amount);
    }
}

Trace:

Step Action Output
1 gateway.Process(new KhaltiPayment(), 1000) "Processing Khalti payment: $1000"
2 gateway.Process(new EBankingPayment(), 500) "Processing E-Banking payment: $500"

In the Real World

  1. eSewa (Nepal):

    • Uses dynamic binding via IPaymentMethod to support Khalti, E-Banking, and mobile payments without hardcoding each type.
    • Example: When you pay via Khalti, eSewa calls KhaltiPayment.ProcessPayment() at runtime.
  2. Pathao (Ride-Hailing):

    • Employs generics (PriorityQueue<Driver>) to assign the nearest available driver based on location.
    • Example: A Driver object is compared using IComparable<Driver> to sort by proximity.
  3. NTC (Telecom Network Design):

    • Uses method overloading for CalculateCost() to handle different cable types (fiber, copper).
    • Example: network.CalculateCost(10, "fiber") vs. network.CalculateCost(5, "copper").
  4. NEPSE (Stock Exchange):

    • Implements interfaces (IStock, ITrader) to standardize price updates across all stock types.
    • Example: A Trader object can buy/sell any IStock (e.g., NabilBank, NMB).
  5. Khalti (Digital Wallet):

    • Leverages operator overloading to add transaction amounts (Transaction + Amount).
    • Example: wallet.Balance += new Transaction(500) updates the balance dynamically.

Exam Tip

  1. Dynamic Binding:

    • Always explain virtual/override with a base class + derived class example.
    • Show upcasting (e.g., Animal animal = new Dog()).
    • Common pitfall: Forgetting virtual in the base class → no polymorphism!
  2. Operator Overloading:

    • Define binary/unary operators clearly.
    • Show operator symbols (+, -, ==) in the method signature.
    • Exam trick: Overload == and != together (they must be consistent).
  3. Generics:

    • Use where T : IComparable<T> for sorting examples.
    • Compare with non-generic collections (e.g., ArrayList vs. List<T>).
  4. Polymorphism Questions:

    • For compile-time polymorphism, show method overloading with different parameters.
    • For runtime polymorphism, use abstract classes/interfaces with override.
  5. Code Structure:

    • Always include:
      • Class/interface definitions.
      • Method signatures with virtual/override.
      • A trace table for dynamic binding examples.
    • Avoid: Hardcoding types in generics (e.g., List<int> instead of List<T> in answers).

Practice Questions (Exam-Style)

  1. Dynamic Binding:

    public class Vehicle {
        public virtual void Start() => Console.WriteLine("Vehicle started.");
    }
    public class Car : Vehicle {
        public override void Start() => Console.WriteLine("Car engine started.");
    }
    

    Question: What output does Vehicle v = new Car(); v.Start(); produce? Answer: "Car engine started." (runtime polymorphism).

  2. Operator Overloading:

    public class Vector {
        public int X, Y;
        public static Vector operator +(Vector a, Vector b) {
            return new Vector { X = a.X + b.X, Y = a.Y + b.Y };
        }
    }
    

    Question: What is the result of new Vector { X=1, Y=2 } + new Vector { X=3, Y=4 }? Answer: Vector { X=4, Y=6 }.

  3. Generics:

    public T Max<T>(T a, T b) where T : IComparable<T> { ... }
    

    Question: Can Max work with string? Answer: Yes, because string implements IComparable<string>.

Based on the TU BCA syllabus for Dot Net Technology (CACS408), unit 10.

Discussion

Loading…