This article will be a concise tutorial on Dependency Injection Pattern and other related topics: the Dependency inversion principle (DIP), Inversion of control (IoC) principle, and Dependency Injection Container (aka IoC container). While short, this tutorial will go into enough breadth and depth to provide a solid overview of the topics. This article is well suited for those who need to master basic concepts fast.
Interview survival tutorial
The goal of this article is to provide a short, concise tutorial about Dependency Injection Pattern and related topics. It can be used as “the first contact tutorial” for those who want to learn about the topic or as “refresher material” for those who want to refresh their knowledge. This article can be used as interview preparation material for those who need to master basic concepts fast, as well.
Topics covered in this tutorial are typically asked of a candidate interviewing for a Senior Software Engineer (.NET) position.
This is a basic tutorial and does not go into all of the finest details. People learn best when presented with knowledge in “concentric circles." In the first circle, they are taught the basis of everything; in the second concentric circle, they go over what they learned in the previous circle and extend that knowledge with more details; then, in the next circle, they do something similar again, etc. This article is meant to be the first pass on the topic for an interested reader.
Topics presented
The following topics are presented,
- Dependency Injection Pattern – DI (*)
- Dependency inversion principle – DIP (**)
- Inversion of control – IoC (***)
- Dependency Injection Container (****)
Dependency Injection Pattern – DI (*)
First of all, “Dependency Injection Pattern” is a SOFTWARE DESIGN PATTERN. It is called a "pattern" because it suggests low-level specific implementation to a specific problem.
The main problem this pattern aims to solve is how to create “loosely coupled” components. It does that by separating the creation of components from their dependencies.
There are four main roles (classes) in this pattern:
- Client. The client is a component/class that wants to use services provided by another component, called a Service.
- Service-Interface. The service interface is an abstraction describing what kind of services the Service component is providing.
- Service. The Service component/class is providing services according to Service-Interface description.
- Injector. Is a component/class that is tasked with creating Client and Service components and assembling them together.
The way it works is that the Client is dependent on Service-Interface IService. The client depends on the IService interface, but has no dependency on the Service itself. Service implements the IService interface and offers certain services that the Client needs. Injector creates both Client and Service objects and assembles them together. We say that Injector “injects” Service into Client.
Below is a class diagram of this pattern.

Here is a sample code of this pattern.
public interface IService {
void UsefulMethod();
}
public class Service: IService {
void IService.UsefulMethod() {
//some usefull work
Console.WriteLine("Service-UsefulMethod");
}
}
public class Client {
public Client(IService injectedService = null) {
//Constructor Injection
_iService1 = injectedService;
}
private IService _iService1 = null;
public void UseService() {
_iService1?.UsefulMethod();
}
}
public class Injector {
public Client ResolveClient() {
Service s = new Service();
Client c = new Client(s);
return c;
}
}
internal class Program {
static void Main(string[] args) {
Injector injector = new Injector();
Client cli = injector.ResolveClient();
cli.UseService();
Console.ReadLine();
}
}
Types of Dependency Injection based on a method of injecting
Often in literature [1] one can find mentioned different types of Dependency Injection, classified based on the method of injecting Service into Client. I think that is an unimportant distinction, since the effect is always the same. That is, reference to Service is being passed to Client, no matter how. But, for completeness, let us explain it.
So, the types of Dependency Injection, are:
- Constructor Injection – Injection is done in Client constructor
- Method Injection – Injection is done via a dedicated method
- Property Injection – Injection is done via public property
Here is the code that demos each type.
public interface IService {
void UsefulMethod();
}
public class Service: IService {
void IService.UsefulMethod() {
//some usefull work
Console.WriteLine("Service-UsefulMethod");
}
}
public class Client {
public Client(IService injectedService = null) {
//1.Constructor Injection
_iService1 = injectedService;
}
public void InjectService(IService injectedService) {
//2.Method Injection
_iService1 = injectedService;
}
public IService Service {
//3.Property Injection
set {
_iService1 = value;
}
}
private IService _iService1 = null;
public void UseService() {
_iService1?.UsefulMethod();
}
}
public class Injector {
public Client ResolveClient() {
Service S = new Service();
//NOTE: This is tutorial/demo code, normally you
//implement only one of these 3 methods
//1.Constructor Injection
Client C = new Client(S);
//2.Method Injection
C.InjectService(S);
//3.Property Injection
C.Service = S;
return C;
}
}
Main point - Client unaware of the type of Service injected
Let us emphasize the main component in this design pattern. It is the fact that Client is completely ignorant of the type of Service injected. It just sees interface IService, and has no clue what version of Service is being injected. Let us look at the following class diagram:

The Client has no knowledge of which service is being injected, whether if it is Service1, Service2, or Service3. That is the desired result. We see that components/classes Client, Service1, Service2, and Service3 are “loosely coupled."
The Client class is now more reusable and testable. One typical usage of this feature is that in the production environment Client is injected with real service Service1, and in the test environment, the Client is injected Service2, which is a mock service created just for testing.
Benefits of this pattern
The benefits of this pattern are:
- Creation of loosely coupled components/classes Client and Service
- Client has no dependency nor knowledge of Service, which makes it more reusable and testable
- Enables parallel development of components/classes Client and Service by different developers/teams, since the boundary between them is clearly defined by the IService interface
- It eases the unit-testing of components.
Disadvantages that this pattern brings are:
- More effort to plan, create, and maintain an interface
- Dependency on Injector to assemble components/classes.
Similar patterns
This pattern is very similar to GoF book Strategy Pattern [2]. The class diagram is practically the same. The difference is in intent: 1) Dependency injection is more like Structural Patten that has the purpose to assemble loosely coupled components and once assembled they usually stay that way during Client lifetime; while 2) Strategy pattern is a Behavior Pattern whose purpose is to offer different algorithms to the problem. These are usually interchangeable during the Client lifetime.
Dependency inversion principle – DIP (**)
So, the “Dependency inversion principle (DIP)” is a SOFTWARE DESIGN PRINCIPLE. It is called “principle” because it provides high-level advice on how to design software products.
DIP is one of five design principles known under the acronym SOLID [3], promoted by Robert C. Martin [5]. The DIP principle states:
- High-level modules should not depend on low-level modules. Both should depend on the abstraction.
- Abstractions should not depend on details. Details should depend on abstractions.
My interpretation is:
While high-level principles talk about “abstraction," we need to translate that into terms in our specific programming environment. In this case, we'll use C#/.NET. Abstractions in C# are realized by interfaces and abstract classes. When talking about “details," the principle means “concrete implementations."
So, basically, that means that DIP promotes the usage of the interfaces in C# and concrete implementations (low-level modules) should depend on interfaces.
Traditional module dependencies look like this:

DIP proposes this new design:

As you can see, some dependencies (arrows) have inverted directions, so that is where the name “inversion” originated.
The goal of DIP is to create “loosely coupled” software modules. Traditionally, high-level modules depend on low-level modules. DIP's goal is to make high-level modules independent of low-level modules’ implementation details. DIP achieves that by introducing an “abstract layer” (in the form of an interface) between them.
Dependency Injection Pattern (*) follows this principle, and is often referred to as closely related to DIP realization. But, the DIP principle is a broader concept and has an influence on other design patterns. For example, when applied to the factory design pattern or Singleton design pattern, it suggests that those patterns should return a reference to an interface, not a reference to an object.
Inversion of control – IoC (***)
Again, “Inversion of control (IoC)” is a SOFTWARE DESIGN PRINCIPLE. It is called “principle” because it provides high-level advice on how to design software products.
In traditional programming, a custom code always has flow control and calls libraries to perform tasks.
The IoC principle proposes that (sometimes) the flow of control should be given to libraries (“framework”), which will call custom code to perform tasks.
When we say “framework,” we mean a specialized, arbitrary complex reusable module/library that is designed for a specific task. Custom code is written in a manner so it can work with that “framework.” We say that the “flow of control is inverted” since now “framework” calls into custom code.
The framework plays the role of the main program in controlling application activity. The main control of the program is inverted, moved away from you to the framework. Inversion of control is a key part of what makes a framework different from a library ([26]).
The IoC principle promotes the development and usage of reusable “software frameworks” that implement common scenarios. Then, problem-specific custom code is written and made to work together with the “framework” to solve a specific task.
While IoC principle is often mentioned in the context of Dependency Injection Pattern (*) which follows it, it is a much broader concept than DIP. For example, an “UI framework” based on event handlers/callback methods also follows IoC principle. See [26], [25], [8] for more explanation.
Dependency Injection Pattern (*) follows this principle, since the normal traditional approach is for the Client to create a Service and establish dependency. Here, control is inverted. That is, the creation of Service and the creation of dependency are delegated to the Injector, which in this case is the “framework."
Dependency Injection Container (****)
So, “Dependency Injection Container (DI Container)” is a SOFTWARE MODULE/LIBRARY that enables automatic Dependency Injection with many advanced options.
In the terminology of IoC principle (***), DI Container has the role of the “framework” so often you will see it referred to as “DI framework." My opinion is that word “framework” is overused, and this leads to confusion (you have ASP MVC framework, DI framework, Entity Framework, etc.).
Often in literature, this is referred to as “IoC Container." But, I think the IoC principle (***) is a broader concept than the DI pattern (*). Here we take the reality of the DI pattern implementation on a large scale. So, “DI Container” is a much better name, but the name “IoC Container” is very popular and is used broadly to mean the same thing.
What is DI Container
Remember DI pattern (*) and the role of the Injector? So, DI Container is an advanced module/library that serves as an Injector for many Services at the same time. It enables the implementation of DI pattern on a large scale, with many advanced functions. DI Containers are a very popular architectural mechanism and many popular frameworks such as ASP MVC plan for and enable the integration of DI Containers.
The most popular DI Containers are Autofac [10], Unity [15], Ninject [16], Castle Windsor [17], etc.
DI Container functions
Typical functions that one DI Container will offer are:
Register Mappings
You need to tell the DI Container mappings between abstraction (interfaces) to concrete implementations (classes) so that it can properly inject proper types. Here you feed the container with the basic info it needs to work.
Mange objects Scope and Lifetime
You need to tell the container what scope and lifetime the object it creates will have.
Typical “lifestyle” patterns are:
- Singleton. A single instance of the object is always used.
- Transient. Every time a new instance of an object is created.
- Scoped. That is typically a singleton pattern per an implicitly or explicitly defined scope.





Nathaniel JonesPosted Apr 5, 2024, 5:40 PM
A+, I really appreciate this tutorial - very well explained, complete with some great references! I've been trying to learn a new ecosystem written in C#. In many cases I was able to find the code I needed within the ecosystem, but had a hard time finding examples of how it was used. Now I understand how+why DI is used to abstract away calling logic.
Tural SuleymaniPosted Aug 14, 2022, 6:24 PM
Good job !