.NET ProgrammingUnit 311 min read

OOP in C: Classes, Inheritance, Polymorphism & Abstraction

Unit 3 of .NET Programming covers the core principles of Object-Oriented Programming (OOP) in C—classes, objects, inheritance, polymorphism, encapsulation, and abstraction—with real-world applications, code examples, and visual traces of how these concepts work in memory and execution.

TAKEAWAYS:

  • Classes and objects are the building blocks of OOP, encapsulating data (fields) and behavior (methods) into reusable blueprints.
  • Inheritance allows code reuse by creating hierarchical relationships between classes (e.g., Animal → Dog).
  • Polymorphism enables one interface to represent different underlying forms (e.g., method overriding or interfaces).
  • Abstraction hides complex implementation details, exposing only essential features (e.g., abstract classes/interfaces).
  • Encapsulation protects data integrity by restricting direct access via access modifiers (public, private, protected).
  • Composition and aggregation model "has-a" relationships, avoiding tight coupling in design.

Core Concepts of OOP in C#

1. Classes and Objects: The Foundation

A class is a blueprint for creating objects, combining data (fields) and behavior (methods). An object is an instance of a class.

Key Components:

  • Fields: Variables that store data (e.g., int age).
  • Properties: Getters/setters for controlled access (e.g., public int Age { get; set; }).
  • Methods: Functions that define behavior (e.g., void Display()).
  • Constructors: Special methods to initialize objects (public Person(string name)).

Example: Person Class

public class Person
{
    // Field
    private string _name;

    // Property
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    // Constructor
    public Person(string name)
    {
        _name = name;
    }

    // Method
    public void Display()
    {
        Console.WriteLine($"Name: {_name}");
    }
}

Visual: Object Creation in Memory

classDiagram
    class Person {
        -_name: string
        +Name: string
        +Person(string)
        +Display()
    }
    Person --> "1" PersonObject : "Instance"

Worked Example: Bank Account

public class BankAccount
{
    private decimal _balance;

    public BankAccount(decimal initialBalance)
    {
        _balance = initialBalance;
    }

    public void Deposit(decimal amount)
    {
        _balance += amount;
    }

    public void Withdraw(decimal amount)
    {
        if (amount <= _balance)
            _balance -= amount;
        else
            Console.WriteLine("Insufficient funds!");
    }

    public decimal GetBalance() => _balance;
}

Trace: Deposit and Withdraw Operations

Step Action _balance (Before) _balance (After)
1 Deposit(500) 1000 1500
2 Withdraw(200) 1500 1300
3 Withdraw(2000) 1300 1300 (Error)

2. Inheritance: Code Reuse Hierarchy

Inheritance allows a derived class (child) to inherit fields/methods from a base class (parent). Uses : BaseClass syntax.

Types of Inheritance in C#:

Type Description Example
Single One class inherits from another. Dog : Animal
Multilevel Chain of inheritance (e.g., Grandparent → Parent → Child). Vehicle → Car → SportsCar
Hierarchical Multiple classes inherit from one base class. Animal → Dog, Animal → Cat
Multiple Not directly supported in C# (use interfaces instead). N/A

Example: Animal and Dog Classes

public class Animal
{
    public void Eat() => Console.WriteLine("Eating...");
}

public class Dog : Animal
{
    public void Bark() => Console.WriteLine("Barking!");
}

Visual: Inheritance Hierarchy

classDiagram
    class Animal {
        +Eat()
    }
    class Dog {
        +Bark()
    }
    Animal <|-- Dog : Inherits

Real-World Tie: Pathao Driver App

  • Base Class: User (fields: Name, Email).
  • Derived Classes: Driver : User, Rider : User.
  • Inheritance Use: Both Driver and Rider inherit Login() and UpdateProfile() from User, but override EarnMoney() differently.

3. Polymorphism: "Many Forms"

Polymorphism allows objects of different classes to be treated as objects of a common superclass. Two types:

  1. Compile-time (Method Overloading): Same method name, different parameters.
  2. Runtime (Method Overriding): Child class provides a specific implementation of a base method.

Example: Method Overriding

public class Shape
{
    public virtual void Draw() => Console.WriteLine("Drawing a shape.");
}

public class Circle : Shape
{
    public override void Draw() => Console.WriteLine("Drawing a circle.");
}

Visual: Polymorphism in Action

sequenceDiagram
    participant Client as Client Code
    participant Shape as Shape
    participant Circle as Circle
    Client->>Shape: Shape s = new Circle();
    Client->>Shape: s.Draw()
    Shape->>Circle: Draw() (Overridden)
    Circle-->>Client: "Drawing a circle."

Worked Example: Ncell Billing System

  • Base Class: Bill (method: CalculateTotal()).
  • Derived Classes: PrepaidBill : Bill, PostpaidBill : Bill.
  • Polymorphism Use: The system calls CalculateTotal() on a List<Bill>, and each type computes its own logic.

4. Abstraction: Hiding Complexity

Abstraction exposes only essential features, hiding implementation details. Achieved via:

  • Abstract Classes: Cannot be instantiated; contain abstract methods (no body).
  • Interfaces: Contracts defining methods a class must implement (no fields or accessors).

Example: Abstract Class

public abstract class Vehicle
{
    public abstract void Start(); // No implementation
    public void Stop() => Console.WriteLine("Vehicle stopped.");
}

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

Example: Interface

public interface ILogger
{
    void Log(string message); // No implementation
}

public class FileLogger : ILogger
{
    public void Log(string message) => Console.WriteLine($"Logging to file: {message}");
}

Visual: Abstraction Layers

classDiagram
    class Vehicle {
        <<abstract>>
        +Start()
        +Stop()
    }
    class Car {
        +Start()
    }
    Vehicle <|-- Car : Inherits
    Vehicle --> ILogger : "Uses"

Real-World Tie: eSewa Payment Gateway

  • Interface: IPaymentMethod (methods: ProcessPayment(), Validate()).
  • Implementations: CreditCardPayment : IPaymentMethod, MobileWalletPayment : IPaymentMethod.
  • Abstraction Use: eSewa’s backend calls ProcessPayment() without knowing the underlying method (credit card vs. Khalti).

5. Encapsulation: Data Protection

Encapsulation restricts direct access to fields using access modifiers:

  • private: Accessible only within the class.
  • public: Accessible everywhere.
  • protected: Accessible within the class and derived classes.
  • internal: Accessible within the same assembly.

Example: Encapsulated BankAccount

public class BankAccount
{
    private decimal _balance;

    public void Deposit(decimal amount)
    {
        if (amount > 0) _balance += amount;
    }

    public decimal GetBalance() => _balance; // Read-only access
}

Visual: Encapsulation Barrier

classDiagram
    class BankAccount {
        -_balance: decimal
        +Deposit(decimal)
        +GetBalance(): decimal
    }
    BankAccount --> "1" ExternalCode : "Cannot access _balance directly"

Real-World Tie: Khalti Transaction Security

  • Encapsulation Use: Khalti’s Transaction class hides the CardNumber field as private and exposes only ProcessPayment() and GetTransactionId().
  • Why? Prevents direct manipulation of sensitive data.

6. Composition vs. Inheritance

Feature Inheritance ("Is-A") Composition ("Has-A")
Relationship Child inherits from parent. Class contains another class.
Coupling Tight (changes in parent affect child). Loose (changes in contained class are isolated).
Use Case Hierarchical modeling (e.g., Dog : Animal). Flexible design (e.g., Car has an Engine).

Example: Composition

public class Engine
{
    public void Start() => Console.WriteLine("Engine started.");
}

public class Car
{
    private Engine _engine = new Engine();

    public void StartCar() => _engine.Start();
}

Visual: Composition Structure

classDiagram
    class Car {
        -_engine: Engine
        +StartCar()
    }
    class Engine {
        +Start()
    }
    Car --> "1" Engine : "Contains"

Real-World Tie: Daraz Order Processing

  • Composition Use: An Order class contains an OrderItem collection (composition) instead of inheriting from Item.
  • Why? Allows orders to have multiple items without tight coupling.

In the Real World

  1. Pathao Driver App

    • Inheritance: Driver and Rider inherit from User (shared fields like Name, Email).
    • Polymorphism: The app calls CalculateEarnings() differently for drivers (per trip) vs. riders (per delivery).
  2. Khalti Payment System

    • Abstraction: IPaymentGateway interface defines ProcessPayment(), implemented by CreditCardGateway and MobileWalletGateway.
    • Encapsulation: Sensitive data like CardCVV is private; only VerifyCVV() is exposed.
  3. Ncell Billing Portal

    • Polymorphism: A List<Bill> contains PrepaidBill and PostpaidBill objects. The system calls GenerateBill() on each, and the correct override executes.
    • Composition: A Customer object contains a BillingAddress object (composition) instead of inheriting from Address.

Exam Tip

  1. Define Key Terms Precisely:

    • Inheritance: "A mechanism where a new class derives properties/methods from an existing class."
    • Polymorphism: "The ability to process objects differently based on their data type or class."
  2. Code Examples Are Mandatory:

    • Always write short but complete code snippets (e.g., abstract class, interface implementation).
    • Trace execution for polymorphism (e.g., show which method is called at runtime).
  3. Diagrams > Text:

    • Draw class diagrams for inheritance/composition.
    • Use sequence diagrams for polymorphism (e.g., Shape s = new Circle(); s.Draw()).
  4. Real-World Mapping:

    • Relate abstract class to eSewa’s PaymentMethod (no direct instantiation).
    • Link interface to Ncell’s IBillGenerator (contract for all bill types).
  5. Common Pitfalls:

    • Multiple Inheritance: C# doesn’t support it directly (use interfaces instead).
    • Access Modifiers: private fields must use properties for controlled access.
    • override vs. new: Use override for runtime polymorphism; new hides base methods (not recommended).
  6. Short-Answer Questions:

    • Difference between abstract and interface:
      • Abstract classes can have implemented methods and fields; interfaces cannot (pre-C# 8).
      • A class can implement multiple interfaces but inherit from only one abstract class.
    • When to use composition over inheritance:
      • Use composition for "has-a" relationships (e.g., Car has an Engine).
      • Use inheritance for "is-a" relationships (e.g., Dog is an Animal).

Visual Summary of OOP Principles

Based on the TU BIM syllabus for .NET Programming (IT275), unit 3.

Discussion

Loading…