Loading...

Encapsulation, Inheritance, and Polymorphism in C#

Learn encapsulation, inheritance, and polymorphism in C# with clear examples to understand core OOP principles and real use cases.

Object-Oriented Programming (OOP) is an approach developed to write reusable, maintainable, and understandable code in software development. The three main pillars of OOP are:

When these three features are used together, a solid architecture can be built in software projects. Let’s now examine each of them individually.


Encapsulation

Encapsulation hides the internal structure of a class from the outside world, exposing only what is necessary. This prevents direct manipulation of data, reduces errors, and ensures that internal logic changes do not affect external code. In C#, it is implemented using access modifiers such as private, protected, internal, and public. Typically, fields are hidden, and controlled access is provided through properties or methods.


public class BankAccount
{
    private decimal _balance; // direct access restricted

    public decimal Balance 
    { 
        get => _balance; 
        private set => _balance = value; 
    }

    public void Deposit(decimal amount)
    {
        if (amount <= 0)
            throw new ArgumentException("Amount must be greater than zero.");
        Balance += amount;
    }

    public void Withdraw(decimal amount)
    {
        if (amount > Balance)
            throw new InvalidOperationException("Insufficient funds.");
        Balance -= amount;
    }
}

In this example, the balance (_balance) cannot be modified directly. The user can only interact with it via Deposit and Withdraw methods. This prevents misuse and ensures data safety.


Inheritance

Inheritance allows a class to derive properties and behaviors from another class. This way, common code is centralized and becomes reusable. The base class defines general behaviors, while derived classes add specialized ones.


public class Animal
{
    public string Name { get; set; } = string.Empty;

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine($"{Name} is barking.");
    }
}

public class Cat : Animal
{
    public void Meow()
    {
        Console.WriteLine($"{Name} is meowing.");
    }
}

class Program
{
    static void Main()
    {
        Dog d = new Dog { Name = "Buddy" };
        d.Eat();  // inherited from Animal
        d.Bark(); // Dog-specific

        Cat c = new Cat { Name = "Misty" };
        c.Eat();  // inherited from Animal
        c.Meow(); // Cat-specific
    }
}

Here, both Dog and Cat inherit from Animal. The shared behavior Eat() is written only once but is available in all subclasses.


Polymorphism

Polymorphism means the same method can behave differently depending on the object. There are two main types of polymorphism:


public class Animal
{
    public string Name { get; set; } = string.Empty;

    public virtual void Speak()
    {
        Console.WriteLine($"{Name} makes a sound.");
    }
}

public class Dog : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} barks.");
    }
}

public class Cat : Animal
{
    public override void Speak()
    {
        Console.WriteLine($"{Name} meows.");
    }
}

class Program
{
    static void Main()
    {
        Animal a1 = new Dog { Name = "Buddy" };
        Animal a2 = new Cat { Name = "Misty" };

        a1.Speak(); // Buddy barks.
        a2.Speak(); // Misty meows.
    }
}

The same Speak() call produces different output depending on the object type. This flexibility is one of the strongest features of OOP.


Example: Book Types

Let’s model books in a library application. Shared properties are defined in the Book class, while each subclass adds its own information or overrides the PrintInfo() method. Additionally, Author and Publisher classes are used to hold related information in separate objects. This demonstrates how inheritance and polymorphism can be combined.


public class Author
{
    public string Name { get; set; } = string.Empty;
    public string Country { get; set; } = string.Empty;
}

public class Publisher
{
    public string Name { get; set; } = string.Empty;
    public string Address { get; set; } = string.Empty;
}

public abstract class Book
{
    public string Title { get; set; } = string.Empty;
    public Author BookAuthor { get; set; } = new Author();
    public Publisher BookPublisher { get; set; } = new Publisher();
    public int Pages { get; set; }
    public string ISBN { get; set; } = string.Empty;
    public int Year { get; set; }

    // To be customized in subclasses
    public abstract void PrintInfo();
}

public class ScienceBook : Book
{
    public string Field { get; set; } = string.Empty; // Physics, Chemistry, Biology

    public override void PrintInfo()
    {
        Console.WriteLine($"[Science Book] {Title} - {BookAuthor.Name}, {Pages} pages");
        Console.WriteLine($"Field: {Field}, ISBN: {ISBN}, Publisher: {BookPublisher.Name}");
    }
}

public class HistoryBook : Book
{
    public string Period { get; set; } = string.Empty; // Ancient, Medieval, etc.

    public override void PrintInfo()
    {
        Console.WriteLine($"[History Book] {Title} - {BookAuthor.Name}, {Pages} pages");
        Console.WriteLine($"Period: {Period}, ISBN: {ISBN}, Publisher: {BookPublisher.Name}");
    }
}

class Program
{
    static void Main()
    {
        var publisher = new Publisher { Name = "Knowledge Press", Address = "New York" };
        var author1 = new Author { Name = "Albert Einstein", Country = "Germany" };
        var author2 = new Author { Name = "Halil Inalcik", Country = "Turkey" };

        Book b1 = new ScienceBook
        {
            Title = "Theory of Relativity",
            BookAuthor = author1,
            BookPublisher = publisher,
            Pages = 250,
            ISBN = "123-456-789",
            Year = 1920,
            Field = "Physics"
        };

        Book b2 = new HistoryBook
        {
            Title = "The Ottoman Empire",
            BookAuthor = author2,
            BookPublisher = publisher,
            Pages = 500,
            ISBN = "987-654-321",
            Year = 1973,
            Period = "Ottoman Era"
        };

        b1.PrintInfo();
        Console.WriteLine();
        b2.PrintInfo();
    }
}

Advantages


TL;DR

  • Encapsulation: Hides internal data and provides controlled access.
  • Inheritance: Properties and behaviors of a base class are passed to subclasses.
  • Polymorphism: The same method can behave differently in different subclasses.

Related Articles