Dependency Injection in C#

If you’re starting to learn C# you may have come around the term Dependency Injection. This blog post will discuss the basics of DI and provide code examples to illustrate how to use DI in C# applications.

The first time I heard “Dependency Injection” I thought it would be some super complex programming magic that would require more brain power than my two brain cells could comprehend. But in fact, the following quote by James Shore sums it up nicely.

“Dependency Injection” is a 25-dollar term for a 5-cent concept.

James Shore

In this post, we’ll look at what Dependency Injection is, why we use it and how you can start using it in your code today.

What is Dependency Injection?

Dependency Injection (DI) is a design pattern that is widely used not just in C# but also languages like Java and even Angular. It refers to the concept of passing in the objects that an object may need (its dependencies) instead of the object having to construct those objects (dependencies) itself. This enables the class to be loosely coupled with its dependencies, making it easier to maintain and test, and less prone to errors.

Looking at a basic example of this may make more sense. Let’s say we have a class MyClass which needs to create and use a super fancy (and super fictional) logger SuperLogger. One way we could achieve this is the following:

public class MyClass {
	private SuperLogger _logger;
	
	// Constructor
	public MyClass() {
		_logger = new SuperLogger();
		_logger.LogInformation("Hello world");
	}
}

We declare _logger which is of type SuperLogger, and then in our constructor, we instantiate a new instance of SuperLogger that we then use to log “Hello World". That’s all well and good. Now, what if another class wants to log its own things as well?

Sure, we can do the same thing in our other class too.

public class MyOtherClass {
	private SuperLogger _logger;
	
	// Constructor
	public MyOtherClass() {
		_logger = new SuperLogger();
		_logger.LogInformation("Hello other world");
	}
}

But…programmers are lazy! We don’t like repeating the same code in multiple places. So, what’s the solution? Dependency Injection!

We can use DI to pass in an object of type SuperLogger to any class that needs to use it, without that class having to instantiate the object itself. We’ll even go one step further by creating an interface that our SuperLogger class will implement.

Let’s see how we go about doing this.

Using Dependency Injection

There are a few different ways we can use DI.

Constructor Injection

We can modify our code above to use DI and have ISuperLogger injected in through its constructor method:

public class MyClass {
	private ISuperLogger _logger;
	
	// Constructor
	public MyClass(ISuperLogger logger) {
		_logger = logger;
		_logger.LogInformation("Hello World");
	}
}

And same goes for our other class:

public class MyOtherClass {
	private ISuperLogger _logger;
	
	// Constructor
	public MyOtherClass(ISuperLogger logger) {
		_logger = logger;
		_logger.LogInformation("Hello other world");
	}
}

In this example, the class accepts a dependency of type ISuperLogger in its constructor. The ISuperLogger dependency is then stored in a private field that can be used by the class. I haven’t explicitly shown what our ISuperLogger interface looks like above but for those wondering, it may look something like this.

interface ISuperLogger 
{
  void LogInformation()
  void LogWarning();
	void LogError();
}

Method Injection

The following example shows how to use a method to inject a dependency into a class. This method can be a class method or an interface method.

public interface ICustomClass {
	private void SetLogger(ISuperLogger logger);
}
public class MyClass {
	private ISuperLogger _logger;
	
	// Constructor
	public MyClass() {
		
	}
	// Setter method
	public void SetLogger(ISuperLogger logger) {
		_logger = logger;
	}
}

In this example, MyClass accepts a dependency of type ISuperLogger through its SetLogger method. The ISuperLogger dependency is then stored in a private field ready to be used by MyClass.

Property Injection

The following example shows how to use property injection to inject a dependency.

public class MyClass {
	public ISuperLogger Logger { get; set; }
	// Constructor
	public MyClass() {
		
	}
}

In this example, MyClass includes the public property Logger, where you can set an instance of a class that implements ISuperLogger.

To wrap up…

In this blog post, we discussed the basics of Dependency Injection and provided code examples to illustrate how to use DI in C# applications. We discussed the three different types of DI – constructor injection, method injection, and property injection. By using DI in your applications, you can make your code more maintainable, testable and less prone to errors.

I guess I should also have a little more faith in my two brain cells next time. 😄