What is dependency inversion principle and how to implement in C#?

The Dependency Inversion Principle (DIP) is one of the five SOLID principles in object-oriented design. It states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Additionally, abstractions should not depend on details ? details should depend on abstractions.

This principle helps reduce tight coupling between classes, making code more flexible, testable, and maintainable. By depending on abstractions rather than concrete implementations, you can easily swap out dependencies without modifying existing code.

Key Rules of Dependency Inversion

  • High-level modules should not depend directly on low-level modules.

  • Both high-level and low-level modules should depend on abstractions (interfaces or abstract classes).

  • Abstractions should not depend on details ? the interface should not know about implementation specifics.

  • Details should depend on abstractions ? concrete classes should implement interfaces.

Dependency Inversion Principle High-Level Module (Notification) IMessage depends on Email SMS Push implements

Example: Before Dependency Inversion

In this example, the Notification class directly depends on concrete Email and SMS classes, violating the DIP −

using System;

namespace BeforeDIP {
   public class Email {
      public string ToAddress { get; set; }
      public string Subject { get; set; }
      public string Content { get; set; }
      
      public void SendEmail() {
         Console.WriteLine("Email sent to: " + ToAddress);
      }
   }
   
   public class SMS {
      public string PhoneNumber { get; set; }
      public string Message { get; set; }
      
      public void SendSMS() {
         Console.WriteLine("SMS sent to: " + PhoneNumber);
      }
   }
   
   public class Notification {
      private Email _email;
      private SMS _sms;
      
      public Notification() {
         _email = new Email();
         _sms = new SMS();
      }
      
      public void Send() {
         _email.SendEmail();
         _sms.SendSMS();
      }
   }
   
   public class Program {
      public static void Main() {
         Notification notification = new Notification();
         notification.Send();
      }
   }
}

The output of the above code is −

Email sent to: 
SMS sent to: 

Example: After Dependency Inversion

Here's the same code refactored to follow DIP by introducing an IMessage interface −

using System;
using System.Collections.Generic;

namespace AfterDIP {
   public interface IMessage {
      void SendMessage();
   }
   
   public class Email : IMessage {
      public string ToAddress { get; set; }
      public string Subject { get; set; }
      public string Content { get; set; }
      
      public Email(string toAddress, string subject, string content) {
         ToAddress = toAddress;
         Subject = subject;
         Content = content;
      }
      
      public void SendMessage() {
         Console.WriteLine($"Email sent to: {ToAddress} with subject: {Subject}");
      }
   }
   
   public class SMS : IMessage {
      public string PhoneNumber { get; set; }
      public string Message { get; set; }
      
      public SMS(string phoneNumber, string message) {
         PhoneNumber = phoneNumber;
         Message = message;
      }
      
      public void SendMessage() {
         Console.WriteLine($"SMS sent to: {PhoneNumber} - {Message}");
      }
   }
   
   public class Notification {
      private ICollection<IMessage> _messages;
      
      public Notification(ICollection<IMessage> messages) {
         _messages = messages;
      }
      
      public void Send() {
         foreach (var message in _messages) {
            message.SendMessage();
         }
      }
   }
   
   public class Program {
      public static void Main() {
         var messages = new List<IMessage> {
            new Email("user@example.com", "Welcome", "Hello World"),
            new SMS("123-456-7890", "Your order is ready")
         };
         
         Notification notification = new Notification(messages);
         notification.Send();
      }
   }
}

The output of the above code is −

Email sent to: user@example.com with subject: Welcome
SMS sent to: 123-456-7890 - Your order is ready

Benefits of Dependency Inversion

Before DIP After DIP
High-level modules depend on concrete low-level modules Both depend on abstractions (interfaces)
Tightly coupled, hard to test and modify Loosely coupled, easy to test and extend
Adding new message types requires modifying existing code New message types can be added without changing existing code
Dependencies are created within the class Dependencies are injected from outside

Conclusion

The Dependency Inversion Principle promotes loose coupling by making high-level modules depend on abstractions rather than concrete implementations. This makes your code more flexible, testable, and maintainable, allowing you to easily add new functionality without modifying existing code.

Updated on: 2026-03-17T07:04:36+05:30

866 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements