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/overrideto resolve method calls at runtime, enabling runtime polymorphism (e.g.,eSewaprocesses different payment methods dynamically). - Operator overloading lets you redefine operators (
+,-,==) for custom classes (e.g.,Complexnumbers 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()forSquare/Circlein NTC’s network design tools). - Abstract classes and interfaces enforce contracts (e.g.,
IPaymentGatewayin Daraz’s checkout system). - Polymorphism reduces code duplication by treating derived objects uniformly (e.g., NEPSE’s stock ticker updates all
IStockimplementations).
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:
virtualkeyword: Marks a method as overrideable in derived classes.overridekeyword: 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.,
Complexnumbers). - Collections (e.g.,
List<T>concatenation).
Rules for Overloading
- Must be a binary or unary operator.
- At least one operand must be of the custom class.
- 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"| BCode:
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
eSewa (Nepal):
- Uses dynamic binding via
IPaymentMethodto support Khalti, E-Banking, and mobile payments without hardcoding each type. - Example: When you pay via Khalti,
eSewacallsKhaltiPayment.ProcessPayment()at runtime.
- Uses dynamic binding via
Pathao (Ride-Hailing):
- Employs generics (
PriorityQueue<Driver>) to assign the nearest available driver based on location. - Example: A
Driverobject is compared usingIComparable<Driver>to sort by proximity.
- Employs generics (
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").
- Uses method overloading for
NEPSE (Stock Exchange):
- Implements interfaces (
IStock,ITrader) to standardize price updates across all stock types. - Example: A
Traderobject can buy/sell anyIStock(e.g.,NabilBank,NMB).
- Implements interfaces (
Khalti (Digital Wallet):
- Leverages operator overloading to add transaction amounts (
Transaction + Amount). - Example:
wallet.Balance += new Transaction(500)updates the balance dynamically.
- Leverages operator overloading to add transaction amounts (
Exam Tip
Dynamic Binding:
- Always explain
virtual/overridewith a base class + derived class example. - Show upcasting (e.g.,
Animal animal = new Dog()). - Common pitfall: Forgetting
virtualin the base class → no polymorphism!
- Always explain
Operator Overloading:
- Define binary/unary operators clearly.
- Show operator symbols (
+,-,==) in the method signature. - Exam trick: Overload
==and!=together (they must be consistent).
Generics:
- Use
where T : IComparable<T>for sorting examples. - Compare with non-generic collections (e.g.,
ArrayListvs.List<T>).
- Use
Polymorphism Questions:
- For compile-time polymorphism, show method overloading with different parameters.
- For runtime polymorphism, use abstract classes/interfaces with
override.
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 ofList<T>in answers).
- Always include:
Practice Questions (Exam-Style)
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).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 }.Generics:
public T Max<T>(T a, T b) where T : IComparable<T> { ... }Question: Can
Maxwork withstring? Answer: Yes, becausestringimplementsIComparable<string>.
Based on the TU BCA syllabus for Dot Net Technology (CACS408), unit 10.
Discussion
Loading…