C#
Constructor injection
Learn to pass dependencies through constructor injection.
Constructor injection
Constructor injection passes the dependency as a parameter to the class's constructor. The received dependency is stored in a private readonly field. This is the most common DI pattern.
Constructor injection guarantees the dependency will not change during the class's lifetime. The readonly keyword ensures the field can only be assigned in the constructor; it cannot be changed afterwards. In tests, fake dependencies can be passed easily.
The readonly field
The dependency is stored as private readonly ILogger _logger;. readonly means the field can only be assigned in the constructor (or at declaration); it cannot be changed afterwards.
This guarantees the dependency's immutability and prevents accidental reassignment. In tests, when you want to swap the real logger for a fake, you just create a new instance and pass it to the constructor; you cannot mutate the existing instance's field.
Exercise Pass two dependencies through the constructor
Define the constructor of the Service class below with the signature public Service(ILogger logger, IRepo repo). In the body assign _logger = logger; and _repo = repo;. In Main create the instance with new Service(new ConsoleLogger(), new FakeRepo()) and call s.DoWork().
Show example solution
public Service(ILogger logger, IRepo repo) { _logger = logger; _repo = repo; }. In Main: Service s = new Service(new ConsoleLogger(), new FakeRepo()); s.DoWork();
Example solution:
using System;
interface ILogger { void Log(string msg); }
interface IRepo { string Get(); }
class ConsoleLogger : ILogger
{
public void Log(string msg) => Console.WriteLine(msg);
}
class FakeRepo : IRepo
{
public string Get() => "Data";
}
class Service
{
private readonly ILogger _logger;
private readonly IRepo _repo;
public Service(ILogger logger, IRepo repo)
{
_logger = logger;
_repo = repo;
}
public void DoWork() => _logger.Log(_repo.Get());
}
class Program
{
static void Main()
{
Service s = new Service(new ConsoleLogger(), new FakeRepo());
s.DoWork();
}
}
Test yourself
-
Complete the constructor parameter
public Service(ILogger ____) { _logger = logger; }
Show answer
public Service(ILogger logger) { _logger = logger; }
-
Where is the dependency stored in constructor injection?
- In a local variable
- In a static field
- In a property
- In a private readonly field
Show answer
In a private readonly field — Correct. The dependency is stored in a private readonly field.
-
Match the pattern to its behavior
- Constructor injection
- Passes via constructor
- Property injection
- Passes via property
- Method injection
- Method parameter
- Service locator
- Class pulls
Next lesson: ServiceCollection