It is the responsibility of every programmer to make apps that function reliably and effectively with little upkeep. In addition, the coding for these apps has to be easily maintained and extendable so that new features may be added to the codebase in future upgrades and releases.
Dependency injection is a recommended technique for writing more readable and reusable code. When considering code reusability and testing, in addition to making it quicker to add new features, loosely linked code is always preferable.
To this purpose, loose coupling is achieved in code through the use of dependency injection in applications. This article will explain dependency injection in C# and how you can implement dependency injection to build loosely coupled code.
1. What is Dependency Injection in C#?
Dependency injection needs familiarity with dependency inversion and inversion of control (IoC) to be fully grasped. Dependency inversion is the practice of making more abstract modules rely on concrete ones.
With inversion of control, .NET developers may switch up the usual procedure. To put it another way, it aids in lowering the reliance on external code. With inversion of control, the object is handed over to the framework, which is then tasked with resolving the dependencies between the various modules and classes.
DI encourages developers to build less tightly connected code by separating concerns amongst modules. More specifically, DI helps programmers write code that is easier to understand and modify by reducing the amount of connection between its various parts. It also makes the code more adaptable, which facilitates testing, error detection, and problem-fixing.
Dependency injection’s primary benefit is that it loosens the connection between different parts of a system, such as modules and components. It also facilitates testing and increases the code’s reusability. Finally, it modifies an app’s workflow by lowering the amount of code for frequently performed operations.
What is Dependency Injection and how many ways we have to perform it?
— Muhammad Waseem ๐ต๐ธ (@mwaseemzakir) July 2, 2023
It is a way of instantiating objects from one location instead of doing manually.
It is used to achieve low coupling in application object dependencies, we have three ways of Injecting the dependencies ๐งตโฌ pic.twitter.com/RUg7PbpNjO
2. Types of Dependency Injection
Here are the three popular types of Dependency injection:
2.1 Constructor Injection
The most prevalent form of dependency injection is the constructor injection. It’s a way to make a class’s constructor take care of obtaining its required components. Each required component is supplied as a separate constructor parameter. If you’re doing constructor dependency injection properly, you won’t be injecting actual classes but rather their corresponding interfaces. The term “interface injection” describes this phenomenon.
Turns out this works! What a hack. Using records to get primary constructors to remove some of the boilerplate around assigning fields when doing constructor injection. #dotnet #csharp pic.twitter.com/TZuEQ2VBW1
— David Fowler (@davidfowl) November 18, 2020
Implementing Dependency Injection Using Constructor Injection
Constructor dependency injection is the most popular method of injecting dependencies. This constructor dependency calls for an argument to be supplied to the client class constructor when creating an object.
When an instance of a class is created, it triggers a method known as the constructor. The client must supply an argument in constructor injection. This verifies the integrity of the client instance or object.
The need is supplied as an argument to the constructor. You may put the injection mechanism to work wherever in the class.
C-sharp code for using constructor injection is as follows:
using System; namespace DependencyInjection { public interface IEmployeeService { void Serve(); } // Initialize Employee1 public class Employee1 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 1 is Initialized."); } } // Initialize Employee2 public class Employee2 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 2 is Initialized."); } } public class Client { // it's constructor injection private IEmployeeService _service; public Client(IEmployeeService service) { _service = service; } public void Serve() { _service.Serve(); } } public class Program { public static void Main(string[] args) { Employee1 employee1 = new Employee1(); // Passing the Employee1 dependency Client client = new Client(employee1); client.Serve(); Employee2 employee2 = new Employee2(); // Passing the Employee2 dependency client = new Client(employee2); client.Serve(); Console.ReadKey(); } } } |
Result:
The Injection happens in the constructor, bypassing the Service that implements the IEmployeeService Interface. The dependencies are assembled by a “Builder” and the Builder’s responsibilities are as follows:
- Knowing the types of each IEmployeeService.
- According to the request, feed the abstract IEmployeeService to the Client.
2.2 Property Injection
Injecting a dependency into a client class (dependent class) via a property is known as “property injection.” The primary benefit of property injection is that it allows you to add dependencies without modifying the class’s existing constructors. Lazy loading is another option for passing this dependency.
In other words, the concrete class is not set up unless the dependent class property is called. Alternatively, a setter method can be used in place of this injection type. Taking the dependence and putting it into a variable is the only job of this function.
Implementing Dependency Injection Using Property Injection
When it comes to Property Dependency Injection, the dependency object needs to be injected by the injector via a public property of the client class. Here in the code below, we will have a look at the example of the same which is written in C#:
using System; namespace DependencyInjection { public interface IEmployeeService { void Serve(); } // Initialize Employee1 public class Employee1 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 1 is Initialized."); } } // Initialize Employee2 public class Employee2 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 2 is Initialized."); } } public class Client { private IEmployeeService _service; //Property Injection public IEmployeeService Service { set { this._service = value; } } public void ServeMethod() { this._service.Serve(); } } public class Program { public static void Main(string[] args) { //creating object Employee1 employee1 = new Employee1(); Client client = new Client(); client.Service = employee1; //passing dependency to property client.ServeMethod(); Employee2 employee2 = new Employee2(); client.Service = employee2; //passing dependency to property client.ServeMethod(); Console.ReadLine(); } } } |
Result:
In the above code, the developer has described a Client class that contains a public property known as Service, and here, an instance can be set for classes named Employee1 and Employee2.
2.3 Method Injection
Method dependency injection is a potent but often overlooked strategy. At runtime, it may be used to alter an object’s behavior without altering the object’s source code. Therefore, the system may be more adaptable. The dependence is supplied as a parameter to the injection procedure.
Implementing Dependency Injection Using Method Injection
using System; namespace DependencyInjection { public interface IEmployeeService { void Serve(); } // Initialize Employee1 public class Employee1 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 1 is Initialized."); } } // Initialize Employee2 public class Employee2 : IEmployeeService { public void Serve() { Console.WriteLine("Employee 2 is Initialized."); } } public class Client { public void ServeMethod(IEmployeeService service) { service.Serve(); } } public class Program { public static void Main(string[] args) { Client client = new Client(); //creating object Employee1 employee1 = new Employee1(); client.ServeMethod(employee1); //passing dependency to method Employee2 employee2 = new Employee2(); client.ServeMethod(employee2); //passing dependency to method Console.ReadLine(); } } } |
Result:
As you can see in the above C# code example, the Client class contains a public method named ServeMethod, in which you can pass an instance of the class Employee1 and Employee2.
Further Read On: .Net Core Dependency Injection With Example
3. Benefits of Dependency Injection
๐ Benefits of Dependency Injection
— Muhammad Waseem ๐ต๐ธ (@mwaseemzakir) July 2, 2023
1.Improved testability
2. Enhanced flexibility
3. Increased modularity
4. Enhanced decoupling
5. Better separation of concerns
Dependency injection is a significant concept in programming, but you might not realize it. In this article, we will go through five reasons why dependency injection is so important for C# developers.
3.1 You can Create Cleaner Code with Dependency Injection.
A growing number of dependencies is a frequent source of frustration for programmers. Creating a global variable to hold a reference to the class or service being used is a typical dependency Injection pattern. For the time being, that is effective. However, things get tricky when you need to manipulate a specific instance of a class or service but there are several instances of that class or service in your code. This issue is resolved with dependency injection, which separates the dependent component from the component providing the dependency.
Producing code that is well-organized and straightforward to fix is a central objective of software engineering. A clean piece of code is one that is easy to read and comprehend. However, this is not the case with tightly connected programs whose dependencies are not injected.
Classes become more cumbersome and less reusable when they must construct their own dependencies or invoke singletons. This causes a proliferation of redundant code.
An object’s dependencies are “injected” into it via dependency injection. This implies fewer static classes are being used for system-wide functionality.
3.2 You can Develop Unit Tests with Dependency Injection.
Unit tests are an excellent method for preventing unexpected crashes in your code. The developer who follows you in your line of work is responsible for making sure your unit testing for an object never fails.
You’re doing it incorrectly if you’re not testing your code. However, testing isn’t always easy and clean. However, mocking dependencies is not always easy. You cannot just simulate the behavior of a database on which you rely.
Using dependency injection effectively may greatly improve the efficiency of your unit tests. You can supply a test double (a fake object or proxy object) for an injected interface when you inject the interfaces of dependents. This implies that you have complete command over the injected dependency:
- The under-test class can receive real-world data.
- Either an error or a null value can be returned.
- Verifying that your class appropriately invokes another method is something you can do.
3.3 Injecting Dependencies Promotes Separation of Concerns.
Dependency injection may be used to isolate individual concrete classes from one another. Injecting interfaces rather than actual classes will do this. This results in software with fewer dependencies.
This method hides the fact that your class relies on a specific concrete implementation of a dependency. All it cares about is that the dependence abides by the rules set forth by the interface.
Maintaining an application is less of a chore when classes are only loosely coupled code to one another. Furthermore, your class instance remains unaffected by any changes to the component’s dependencies.
The maintainability of programming is enhanced through dependency injection. The complexity in software development is well-known. The nature of code is intricate and dynamic. To simplify the development process is a constant goal for developers. Dependency injection is a method for making code more easily maintained.
3.4 Dependency Injection Improves Code Maintainability
You have a web app that stores data in MySQL. Someone then decides that the MS SQL database should be used for the website. Yes, if your database layer is completely separated from everything else behind an interface. A new database implementation just requires re-creating the database layer. However, it will be hard to justify the lengthy downtime required to switch databases if SQL code is scattered across the whole service.
The time and resources needed to make changes to the code are directly related to how easily the code can be maintained.
3.5 Code Configuration is Consolidated via Dependency Injection.
Dependency injection, or DI, is a popular technique, but it can be difficult to use at first. Developing an interface, building, and connecting individual elements is a common process. Thankfully, there’s a simpler solution.
A container that supports Inversion of Control (IoC) can be used. An IoC container just requires you to tell it what kinds of objects you need and how to build them. It’s also useful for connecting various electronic components together.
IoC containers allow for dynamic application composition. The utilization of dependency injection containers can also be centralized. This implies that all dependent setups may be handled by a single class, or at most a small number of classes.
As a result, if you need to make a modification to a dependency used elsewhere in the program, you’ll only have to do it once in the code.
4. Summary
When it comes to controlling how classes communicate inside an application, dependency injection (DI) is unrivaled. The method is widely used and crucial in several contexts. Dependency injection is a technique used to improve the development, maintenance, and adaptability of objects and services.
This is a method of programming. We need a means of segmenting and isolating code in order to create programs that are both scalable and manageable. In this article, We discussed the advantages of DI and how it may aid in the creation of modular and isolated code.
FAQs
What is Loose Coupling in C#?
Loose coupling in C# is an excellent object-oriented programming practice that enables the developers to have components in their code that have little or no information about the implementation and internal workings of other components. In this process, the communication is carried out via interfaces.
What is Constructor in C Sharp?
In C#, the constructor is a special method that automatically gets invoked in a class whenever a class instance is created. Similar to other methods, a constructor also comes with a unique set of instructions that can be executed at the time of creating an object. Besides this, a constructor can be used by the developers to assign initial values to the same class data members.
What are the Advantages of Using Dependency Injection in C#?
One of the biggest advantages of dependency injection in C# is that it can make code easier to test and maintain. Besides, coding here is more modular and it can help the developers to reduce dependencies between the different classes of the program in order to improve the quality of the code.
What are Some Common Dependency Injection Frameworks in C#?
In C#, some of the most common and popularly used dependency injection frameworks by developers are Unity, Autofac, and Ninject. There are frameworks that can be used by the .NET app development companies to manage and inject dependencies in a software system.
Comments
Leave a message...