Dependency Inversion Principle (DIP) & Dependency Injection (DI) in C# (Easy Examples)
Abdullah Al Mamun Akand

Abdullah Al Mamun Akand @mamun_akand

About: ICPC Regionalist | .NET Core | Web API | LINQ | C# | SQL | C++ | Competitive Programmer

Location:
Dhaka, Bangladesh
Joined:
Feb 14, 2025

Dependency Inversion Principle (DIP) & Dependency Injection (DI) in C# (Easy Examples)

Publish Date: Feb 14
10 2

🔹Understanding Dependency Inversion Principle


What is DIP?

DIP is a principle in SOLID design that says:

  • High-level code (business logic) should not depend on low-level code (implementation details).
  • Both should depend on an abstraction (interface or abstract class).

Why is DIP Important?

✅ Makes the code flexible – you can swap implementations easily.

Reduces dependencies – high-level modules don’t care about the details.

✅ Makes the code easier to test – we can use mock implementations.


🔹 Real-Life Example (Pizza Ordering System)

Imagine a restaurant with a Waiter and a Chef.

  • Bad Design (Without DIP):

    • The Waiter (business logic) goes to a specific chef (implementation) to make a pizza.
    • If the chef changes, the waiter also needs to change! ❌
  • Good Design (With DIP):

    • The Waiter doesn’t care who the chef is.
    • Instead, the Waiter follows an interface (IChef) to request the pizza.
    • Now, any chef (ItalianChef, RobotChef) can cook without changing the waiter! ✅

🔹 Without DIP (Bad Design - Tightly Coupled Code)

// Low-Level Module (Concrete Implementation)
public class ItalianChef
{
    public void MakePizza()
    {
        Console.WriteLine("Italian Chef is making a pizza!");
    }
}

// High-Level Module (Directly Dependent on Low-Level)
public class Waiter
{
    private ItalianChef _chef; // Direct dependency ❌

    public Waiter()
    {
        _chef = new ItalianChef(); // Hardcoded dependency ❌
    }

    public void TakeOrder()
    {
        _chef.MakePizza(); // Tightly coupled ❌
    }
}
Enter fullscreen mode Exit fullscreen mode

❌ Problems:

  • The Waiter is directly tied to ItalianChef.
  • If we want a RobotChef, we must change the Waiter class.
  • Hard to maintain, test, and extend.

🔹 With DIP (Good Design - Loosely Coupled Code)

Step 1: Create an Interface (Abstraction)

public interface IChef
{
    void MakePizza();
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Implement Different Chef Variants

public class ItalianChef : IChef
{
    public void MakePizza()
    {
        Console.WriteLine("Italian Chef is making a pizza!");
    }
}

public class RobotChef : IChef
{
    public void MakePizza()
    {
        Console.WriteLine("Robot Chef is making a pizza with automation!");
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Modify Waiter to Depend on an Interface

public class Waiter
{
    private IChef _chef; // Uses abstraction ✅

    public Waiter(IChef chef) // Inject dependency
    {
        _chef = chef;
    }

    public void TakeOrder()
    {
        _chef.MakePizza(); // Works with any chef ✅
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Use Dependency Injection

class Program
{
    static void Main()
    {
        IChef chef = new ItalianChef(); // Or new RobotChef();
        Waiter waiter = new Waiter(chef);
        waiter.TakeOrder(); // Output: Italian Chef is making a pizza!
    }
}
Enter fullscreen mode Exit fullscreen mode

✅ Now, the Waiter can work with ANY Chef (ItalianChef, RobotChef, etc.) without changing the Waiter class!


🔹 Understanding Dependency Injection (DI)

What is DI?

Dependency Injection is a technique where dependencies (like IChef) are provided from the outside instead of being created inside the class.

DI Techniques in C#

1️⃣ Constructor Injection (Best Practice)

   public class Waiter
   {
       private IChef _chef;

       public Waiter(IChef chef) // Injected via constructor
       {
           _chef = chef;
       }
   }
Enter fullscreen mode Exit fullscreen mode

2️⃣ Property Injection

   public class Waiter
   {
       public IChef Chef { get; set; } // Injected via property
   }
Enter fullscreen mode Exit fullscreen mode

3️⃣ Method Injection

   public class Waiter
   {
       public void TakeOrder(IChef chef) // Injected via method
       {
           chef.MakePizza();
       }
   }
Enter fullscreen mode Exit fullscreen mode

🔹 Combining DIP & DI for a Powerful System

1️⃣ DIP makes code loosely coupled

2️⃣ DI injects dependencies dynamically

3️⃣ Together, they make the code flexible & testable!

Example with a DI Container (ASP.NET Core)

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IChef, ItalianChef>(); // Inject ItalianChef
builder.Services.AddScoped<Waiter>();

var app = builder.Build();
Enter fullscreen mode Exit fullscreen mode

Now, the framework automatically injects ItalianChef into Waiter!


Key Takeaways

Concept Explanation
DIP (Dependency Inversion Principle) High-level modules should not depend on low-level modules; both should depend on abstractions.
DI (Dependency Injection) A technique to provide dependencies from the outside instead of creating them inside the class.
Interface Role Interfaces act as the middle layer, allowing flexibility.
Best Practice Use DIP + DI together for loosely coupled, maintainable, and testable code.

Outcome

"DIP & DI = More Flexibility + Less Maintenance!"

If you follow these principles, your code will be scalable, reusable, and easy to modify!

Comments 2 total

  • Mohammed Chami
    Mohammed ChamiFeb 18, 2025

    Dependency injection is the industry-standard way of implementing dependency inversion. Do not mix the two; one is a principle and the other refers to the implementation of this principle.
    Thanks for sharing 🍀

    • Abdullah Al Mamun Akand
      Abdullah Al Mamun AkandFeb 19, 2025

      Exactly, I tried to present the both in one place, as it all comes together here. That’s why DI was briefly explained as well. Thanks!

Add comment