A Complete Guide - Interfaces vs Abstract Classes in C#

Last Updated: 03 Jul, 2025   
  YOU NEED ANY HELP? THEN SELECT ANY TEXT.

Interfaces vs Abstract Classes in C#

When designing object-oriented software, developers often need to define the structure and behavior of classes without providing complete implementation. In C#, this is achieved using interfaces and abstract classes. Both serve the purpose of defining a protocol for what a class can do, but they do so in different ways, catering to different design scenarios.

Interfaces

Definition: An interface in C# is a contract that defines a set of methods, properties, events, or indexers, which the implementing class or struct must provide. Unlike classes, interfaces cannot contain any implementation details—only the declarations.

Declaration:

public interface IAnimal
{ void Sleep(); void Eat(string food); int NumberOfLegs { get; set; }
}

Implementation: Any class that implements an interface must provide concrete implementations for all the methods and properties defined in the interface.

public class Dog : IAnimal
{ public int NumberOfLegs { get; set; } public void Eat(string food) { Console.WriteLine($"Dog is eating {food}."); } public void Sleep() { Console.WriteLine("Dog is sleeping."); }
}

Key Features:

  • No Method Implementation: Interfaces contain only declarations, no concrete method implementations.
  • Multiple Inheritance: A class can inherit from multiple interfaces. This allows a class to implement methods from various sources.
  • No State: Interfaces do not contain any state (fields).
  • Access Modifiers: All members of an interface are implicitly public. Using other access modifiers results in a compile-time error.
  • Polymorphism: Interfaces enable polymorphic behavior, allowing objects to be treated uniformly regardless of their specific implementing type.

Abstract Classes

Definition: An abstract class in C# is a class that cannot be instantiated on its own and must be inherited by other classes. It can contain both abstract (unimplemented) and non-abstract (implemented) methods, properties, and fields.

Declaration:

public abstract class Animal
{ public int NumberOfLegs { get; set; } public abstract void Sleep(); public virtual void Eat(string food) { Console.WriteLine($"Animal is eating {food}."); }
}

Implementation: An abstract class serves as a base class that provides some implementation while still requiring derived classes to implement abstract members.

public class Cat : Animal
{ public override void Sleep() { Console.WriteLine("Cat is sleeping."); } // Optionally override or use the inherited Eat method
}

Key Features:

  • Partial Implementation: Abstract classes can provide some concrete method implementations while leaving others abstract.
  • Single Inheritance: A class can inherit only from one abstract class.
  • State: Abstract classes can contain fields, constructors, and non-abstract methods.
  • Access Modifiers: Abstract classes can have members with any access modifiers (public, protected, private, etc.).
  • Polymorphism: Abstract classes also enable polymorphism, allowing objects to be treated through their base class type.

Choosing Between Interfaces and Abstract Classes

Interfaces:

  • Suitable for defining a common protocol or contract without any implementation details.
  • Ideal for multiple inheritance scenarios since a class can implement multiple interfaces.
  • Useful for defining behavior that is orthogonal to the class hierarchy.

Abstract Classes:

  • Best for scenarios where the derived classes share common functionality.
  • Appropriate when you need to share code among several closely related classes.
  • Useful when you want to define default behavior that can be overridden by subclasses.

Practical Considerations

  • Design Goals: Consider the design goals of your application. Interfaces are excellent for defining behavior expectations, while abstract classes are better for sharing code.
  • Reusability: Use interfaces for maximum reusability and flexibility. Use abstract classes when there's a clear hierarchy and shared behavior.
  • Complexity: Avoid unnecessary complexity by using the simplest construct that meets your needs.

Conclusion

Interfaces and abstract classes are powerful tools in C# for defining the structure and behavior of classes. Understanding their differences and appropriate use cases enables developers to design robust and maintainable software systems.


Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement Interfaces vs Abstract Classes in C#


Interfaces vs. Abstract Classes in C#: Complete Examples for Beginners

1. Overview

In C#, interfaces and abstract classes are both used to achieve abstraction, a fundamental concept in object-oriented programming. However, they serve different purposes and are used in distinct scenarios.

  • Interfaces: Define a contract that other classes must implement. They specify what a class can do, not how it does it.
  • Abstract Classes: Provide a common base class with some implementations shared by derived classes. They can define both abstract methods (without implementation) and concrete methods (with implementation).

2. Key Differences

| Feature | Interfaces | Abstract Classes | |--------------------------------|-------------------------------------------|--------------------------------------------------------------------------| | Multiple Inheritance | Yes (a class can implement multiple interfaces) | No (a class can inherit only one abstract class) | | Implementation Details | No implementation (except default interfaces in C# 8+) | Can contain both abstract methods and concrete methods | | Constructor | No constructors | Can have constructors | | Access Modifiers | Members are always public (C# 8 onwards, members default to public) | Members can have various access modifiers (public, protected, etc.) | | Usage | Define capabilities or APIs | Provide a common base implementation and shared code | | Performance | Slightly performance overhead due to method dispatch | Can be slightly more efficient as it can contain implementation |

3. Practical Examples

Let's implement both interfaces and abstract classes to illustrate their usage.

3.1. Using Interfaces

Suppose we want to create a system for different types of vehicles, and all vehicles must know how to Start() and Stop().

using System; // Define the IVehicle interface
public interface IVehicle
{ // Methods that must be implemented by any class that implements IVehicle void Start(); void Stop();
} // Implement the interface in a Car class
public class Car : IVehicle
{ public void Start() { Console.WriteLine("Car is starting."); } public void Stop() { Console.WriteLine("Car is stopping."); }
} // Implement the interface in a Motorcycle class
public class Motorcycle : IVehicle
{ public void Start() { Console.WriteLine("Motorcycle is starting."); } public void Stop() { Console.WriteLine("Motorcycle is stopping."); }
} class Program
{ static void Main() { IVehicle myCar = new Car(); IVehicle myMotorcycle = new Motorcycle(); myCar.Start(); // Output: Car is starting. myCar.Stop(); // Output: Car is stopping. myMotorcycle.Start(); // Output: Motorcycle is starting. myMotorcycle.Stop(); // Output: Motorcycle is stopping. }
}

Explanation:

  • IVehicle Interface: Defines the Start() and Stop() methods that any vehicle must implement.
  • Car and Motorcycle Classes: Both implement the IVehicle interface, providing their specific implementations of Start() and Stop().
  • Polymorphism: We can use the IVehicle interface type to declare variables and pass different vehicle types, demonstrating polymorphism.

3.2. Using Abstract Classes

Now, let's create a system where we have different types of bank accounts, and all accounts must calculate Interest(), but some shared functionality can be provided.

using System; // Define the BankAccount abstract class
public abstract class BankAccount
{ // Properties public string AccountHolderName { get; set; } public double Balance { get; protected set; } // Constructor public BankAccount(string accountHolderName, double initialBalance) { AccountHolderName = accountHolderName; Balance = initialBalance; } // Abstract method: Must be implemented by derived classes public abstract double CalculateInterest(); // Concrete method: Provides common functionality 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 funds."); } }
} // Implement the abstract class in a SavingsAccount class
public class SavingsAccount : BankAccount
{ public double InterestRate { get; set; } public SavingsAccount(string accountHolderName, double initialBalance, double interestRate) : base(accountHolderName, initialBalance) { InterestRate = interestRate; } public override double CalculateInterest() { return Balance * InterestRate; }
} // Implement the abstract class in a CheckingAccount class
public class CheckingAccount : BankAccount
{ public double OverdraftLimit { get; set; } public CheckingAccount(string accountHolderName, double initialBalance, double overdraftLimit) : base(accountHolderName, initialBalance) { OverdraftLimit = overdraftLimit; } public override double CalculateInterest() { // Checking accounts might not earn interest return 0; } public new void Withdraw(double amount) { double totalAvailable = Balance + OverdraftLimit; if (amount <= totalAvailable) { Balance -= amount; Console.WriteLine($"Withdrew ${amount}. New balance: ${Balance}."); } else { Console.WriteLine("Insufficient funds."); } }
} class Program
{ static void Main() { SavingsAccount savings = new SavingsAccount("Alice", 1000, 0.05); CheckingAccount checking = new CheckingAccount("Bob", 500, 200); savings.Deposit(200); savings.Withdraw(50); double savingsInterest = savings.CalculateInterest(); Console.WriteLine($"Savings account interest: ${savingsInterest}."); checking.Withdraw(600); // Overdraft within limit checking.Withdraw(900); // Overdraft exceeds limit double checkingInterest = checking.CalculateInterest(); Console.WriteLine($"Checking account interest: ${checkingInterest}."); }
}

Explanation:

  • BankAccount Abstract Class: Provides common properties (AccountHolderName, Balance) and methods (Deposit(), Withdraw()). It declares an abstract method CalculateInterest() that must be implemented by derived classes.
  • SavingsAccount and CheckingAccount Classes: Derive from BankAccount and provide their specific implementations of CalculateInterest(). The CheckingAccount also overrides the Withdraw() method to include overdraft functionality.
  • Shared Functionality: Methods like Deposit() and Withdraw() are shared across different account types, demonstrating code reuse.
  • Polymorphism: We can use the BankAccount type to declare variables and pass different account types.

4. When to Use Interfaces vs. Abstract Classes

  • Use Interfaces When:

    • You need to define a contract for multiple, unrelated classes.
    • You want to promote loose coupling and flexibility.
    • Multiple classes need to implement the same functionality independently.
    • You need to update the contract without breaking existing implementations (e.g., in different assemblies).
  • Use Abstract Classes When:

    • You need to share code among related classes.
    • You want to provide a common base class with some default implementations.
    • You need to control the creation of objects (e.g., using a factory pattern).
    • You want to define a template for subclasses with some shared behavior.

5. Key Points to Remember

  • Interfaces are ideal for defining capabilities or APIs that can be implemented by any class, regardless of where it fits within the class hierarchy.
  • Abstract Classes are useful when you have a common base class with shared code and specific methods that must be implemented by derived classes.
  • C# 8+ introduced default interface implementations, allowing interfaces to have method bodies, blurring the line slightly between interfaces and abstract classes.
  • Use composition to combine interfaces and abstract classes for even more flexibility and reusability.

6. Additional Resources

Login to post a comment.