C# Abstraction

Introduction

Abstraction is one of the four fundamental principles of object-oriented programming (OOP). It involves the process of hiding the implementation details and showing only the essential features of an object. Abstraction focuses on the what rather than the how. It helps in reducing complexity by allowing the user to interact with an object at a higher level of abstraction.

In C#, abstraction is achieved using abstract classes and interfaces.

Abstract Classes

An abstract class is a class that cannot be instantiated and is typically used as a base class. It can contain abstract methods (without implementation) and non-abstract methods (with implementation). Abstract methods must be implemented by derived classes.

Syntax

public abstract class AbstractClassName
{
    // Abstract method (no implementation)
    public abstract void AbstractMethod();

    // Non-abstract method (with implementation)
    public void RegularMethod()
    {
        // Method body
    }
}

Example

Let’s consider an example to demonstrate the concept of an abstract class.

using System;

namespace AbstractionExample
{
    // Abstract base class
    public abstract class Shape
    {
        // Abstract method (must be implemented by derived classes)
        public abstract void Draw();

        // Non-abstract method (can be inherited or overridden)
        public void Display()
        {
            Console.WriteLine("Displaying the shape.");
        }
    }

    // Derived class
    public class Circle : Shape
    {
        // Implementation of the abstract method
        public override void Draw()
        {
            Console.WriteLine("Drawing a circle.");
        }
    }

    // Derived class
    public class Rectangle : Shape
    {
        // Implementation of the abstract method
        public override void Draw()
        {
            Console.WriteLine("Drawing a rectangle.");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Shape shape;

            shape = new Circle();
            shape.Draw(); // Outputs: Drawing a circle.
            shape.Display(); // Outputs: Displaying the shape.

            shape = new Rectangle();
            shape.Draw(); // Outputs: Drawing a rectangle.
            shape.Display(); // Outputs: Displaying the shape.
        }
    }
}

Output

Drawing a circle.
Displaying the shape.
Drawing a rectangle.
Displaying the shape.

Interfaces

An interface is a contract that defines a set of methods and properties that the implementing class must provide. Interfaces do not contain any implementation. A class can implement multiple interfaces, providing a way to achieve multiple inheritance.

Syntax

public interface InterfaceName
{
    // Method signature
    void MethodName();

    // Property signature
    int PropertyName { get; set; }
}

Example

Let’s consider an example to demonstrate the concept of an interface.

using System;

namespace AbstractionExample
{
    // Interface
    public interface IDrawable
    {
        void Draw();
    }

    // Implementing the interface
    public class Circle : IDrawable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing a circle.");
        }
    }

    // Implementing the interface
    public class Rectangle : IDrawable
    {
        public void Draw()
        {
            Console.WriteLine("Drawing a rectangle.");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IDrawable drawable;

            drawable = new Circle();
            drawable.Draw(); // Outputs: Drawing a circle.

            drawable = new Rectangle();
            drawable.Draw(); // Outputs: Drawing a rectangle.
        }
    }
}

Output

Drawing a circle.
Drawing a rectangle.

Benefits of Abstraction

  1. Simplification: Abstraction reduces complexity by hiding unnecessary details and exposing only the relevant features.
  2. Reusability: Abstract classes and interfaces promote code reuse by defining common methods and properties that can be shared across multiple classes.
  3. Maintainability: Abstraction helps in maintaining the code by separating the implementation from the interface. Changes in implementation do not affect the interface.
  4. Flexibility: Interfaces provide a way to achieve multiple inheritance, allowing a class to implement multiple interfaces.

Real-World Example

Consider a real-world example of a banking system where different types of accounts share common features but have their own specific behaviors.

Abstract Class Example

using System;

namespace BankingExample
{
    // Abstract base class
    public abstract class BankAccount
    {
        public string AccountNumber { get; set; }
        public double Balance { get; set; }

        public BankAccount(string accountNumber, double initialBalance)
        {
            AccountNumber = accountNumber;
            Balance = initialBalance;
        }

        // Abstract method to be implemented by derived classes
        public abstract void CalculateInterest();

        // Non-abstract method
        public void Deposit(double amount)
        {
            Balance += amount;
            Console.WriteLine($"Deposited {amount}. New balance: {Balance}");
        }

        // Non-abstract method
        public void Withdraw(double amount)
        {
            if (amount <= Balance)
            {
                Balance -= amount;
                Console.WriteLine($"Withdrew {amount}. New balance: {Balance}");
            }
            else
            {
                Console.WriteLine("Insufficient balance.");
            }
        }
    }

    // Derived class
    public class SavingsAccount : BankAccount
    {
        public SavingsAccount(string accountNumber, double initialBalance)
            : base(accountNumber, initialBalance)
        {
        }

        // Implementation of the abstract method
        public override void CalculateInterest()
        {
            double interest = Balance * 0.04;
            Balance += interest;
            Console.WriteLine($"Interest added. New balance: {Balance}");
        }
    }

    // Derived class
    public class CheckingAccount : BankAccount
    {
        public CheckingAccount(string accountNumber, double initialBalance)
            : base(accountNumber, initialBalance)
        {
        }

        // Implementation of the abstract method
        public override void CalculateInterest()
        {
            // No interest for checking accounts
            Console.WriteLine("No interest for checking accounts.");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            BankAccount savings = new SavingsAccount("S123", 1000);
            savings.CalculateInterest(); // Outputs: Interest added. New balance: 1040
            savings.Deposit(500);
            savings.Withdraw(300);

            BankAccount checking = new CheckingAccount("C123", 1000);
            checking.CalculateInterest(); // Outputs: No interest for checking accounts.
            checking.Deposit(500);
            checking.Withdraw(300);
        }
    }
}

Output

Interest added. New balance: 1040
Deposited 500. New balance: 1540
Withdrew 300. New balance: 1240
No interest for checking accounts.
Deposited 500. New balance: 1500
Withdrew 300. New balance: 1200

Interface Example

using System;

namespace BankingExample
{
    // Interface
    public interface IAccount
    {
        void Deposit(double amount);
        void Withdraw(double amount);
        void CalculateInterest();
    }

    // Implementing the interface
    public class SavingsAccount : IAccount
    {
        public string AccountNumber { get; set; }
        public double Balance { get; set; }

        public SavingsAccount(string accountNumber, double initialBalance)
        {
            AccountNumber = accountNumber;
            Balance = initialBalance;
        }

        public void Deposit(double amount)
        {
            Balance += amount;
            Console.WriteLine($"Deposited {amount}. New balance: {Balance}");
        }

        public void Withdraw(double amount)
        {
            if (amount <= Balance)
            {
                Balance -= amount;
                Console.WriteLine($"Withdrew {amount}. New balance: {Balance}");
            }
            else
            {
                Console.WriteLine("Insufficient balance.");
            }
        }

        public void CalculateInterest()
        {
            double interest = Balance * 0.04;
            Balance += interest;
            Console.WriteLine($"Interest added. New balance: {Balance}");
        }
    }

    // Implementing the interface
    public class CheckingAccount : IAccount
    {
        public string AccountNumber { get; set; }
        public double Balance { get; set; }

        public CheckingAccount(string accountNumber, double initialBalance)
        {
            AccountNumber = accountNumber;
            Balance = initialBalance;
        }

        public void Deposit(double amount)
        {
            Balance += amount;
            Console.WriteLine($"Deposited {amount}. New balance: {Balance}");
        }

        public void Withdraw(double amount)
        {
            if (amount <= Balance)
            {
                Balance -= amount;
                Console.WriteLine($"Withdrew {amount}. New balance: {Balance}");
            }
            else
            {
                Console.WriteLine("Insufficient balance.");
            }
        }

        public void CalculateInterest()
        {
            // No interest for checking accounts
            Console.WriteLine("No interest for checking accounts.");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            IAccount savings = new SavingsAccount("S123", 1000);
            savings.CalculateInterest(); // Outputs: Interest added. New balance: 1040
            savings.Deposit(500);
            savings.Withdraw(300);

            IAccount checking = new CheckingAccount("C123", 1000);
            checking.CalculateInterest(); // Outputs: No interest for checking accounts.
            checking.Deposit(500);
            checking.Withdraw(300);
        }
    }
}

Output

Interest added. New

 balance: 1040
Deposited 500. New balance: 1540
Withdrew 300. New balance: 1240
No interest for checking accounts.
Deposited 500. New balance: 1500
Withdrew 300. New balance: 1200

Conclusion

Abstraction in C# is a powerful concept that helps in simplifying complex systems by focusing on high-level operations while hiding the underlying details. Abstract classes and interfaces are the primary tools for achieving abstraction. By using these tools, you can create more modular, reusable, and maintainable code. Understanding abstraction is crucial for designing robust and flexible object-oriented applications.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top