DEV Community

Cover image for Inversion of Control (IoC)
Tharindu Lakshan
Tharindu Lakshan

Posted on

Inversion of Control (IoC)

Explained

Inversion of Control is a design principle where the flow of control in a system is inverted. This means that the control over the flow of execution is taken away from the user (developer) and is instead controlled by an external component (such as a framework or an IoC container).

In simpler terms: Instead of the application controlling when and how various actions occur, the external framework or system controls the flow and triggers the necessary actions.

furthermore

  • Inversion of Control (IoC or IOC) describes a system that follows the Hollywood Principle. That is, the flow of control within the application is not controlled by the application itself, but rather by the underlying framework.

Code Example for IoC

For this concept, let's look at a hypothetical task processor and task service. The task processor (TaskProcessor) will get called in the main application to perform a task. Then, it will get an injected implementation of the interface, and it will call the PerformTask() on the injected implementation. The task processor doesn't have a specific implementation in its code - it just knows that it needs an implementation that fulfills a specific interface. The main application is responsible for injecting in the specific implementation.

  • MainProgram is the starting participant that creates an instance of TaskProcessor.
  • TaskProcessor is activated, indicating that it is in the process of being executed.
  • TaskProcessorthen injects an instance of ITaskServiceinto itself, provided by MainProgram. In this example, it could be an instance of TaskService.
  • TaskProcessor calls the PerformTask() method on the injected ITaskService.
  • TaskService is activated to execute the PerformTask() method. TaskServicereturns the result to TaskProcessor.
  • Finally, TaskProcessorreturns the result to MainProgram, and it is deactivated.

The Flow of IoC
Let's look at this situation with a sequence diagram.

Image description
In this sequence diagram:

In this example:

  • ITaskServiceis an interface representing a service with a PerformTask() method.

  • TaskServiceis a concrete implementation of ITaskService.

  • TaskProcessoris a class that depends on ITaskService. Instead of creating an instance of TaskServicewithin TaskProcessor, it receives an instance of ITaskServicethrough constructor injection.

  • In the Main() method, an instance of TaskServiceis created and passed to the constructor of TaskProcessor.

  • The control flow is inverted because TaskProcessordoesn't create or manage its dependencies; they are injected from the outside

Code Sample of IoC
This is a sample of what inversion of control could look like using C#

using System;

// Interface defining the task service
public interface ITaskService
{
    string PerformTask(); 
}

// Concrete implementation of the task service
public class TaskService : ITaskService
{
    public string PerformTask()
    {
        // Task logic here
        return "Task completed";
    }
}

// Class that depends on ITaskService
public class TaskProcessor
{
    private readonly ITaskService taskService;

    // Constructor injection of the dependency
    public TaskProcessor(ITaskService taskService)
    {
        this.taskService = taskService;
    }

    public string ProcessTask()
    {
        // Using the injected dependency
        return taskService.PerformTask();
    }
}


class Program
{
    static void Main()
    {
        // Setting up the dependency manually (IoC container can replace this)
        ITaskService taskService = new TaskService();
        TaskProcessor taskProcessor = new TaskProcessor(taskService);

        // Using the TaskProcessor without worrying about creating its dependencies
        string result = taskProcessor.ProcessTask();
        Console.WriteLine(result);
    }
}
Enter fullscreen mode Exit fullscreen mode

The Hollywood Principle

Image description

The Hollywood Principle is a key concept in software design, particularly in frameworks, where control over the flow of execution is delegated to an external system. Instead of the developer's code explicitly dictating what happens and when, the system (framework, library, or container) decides the flow and invokes developer-defined methods or events as needed.

This principle closely relates to the Dependency Inversion Principle (DIP) and emphasizes Inversion of Control (IoC), where high-level code does not depend on low-level details but instead interacts through abstractions.

Key Characteristics

Event-Driven Programming:

  • Code responds to external events (like HTTP requests, button clicks, or lifecycle hooks).
  • The developer provides methods or handlers that are triggered by the system at the appropriate times.

Control Delegation:

  • The framework owns the execution flow, and the developer writes code to plug into predefined hooks or events.

Decouples: application logic from the underlying execution mechanism, enabling flexibility, reusability, and extensibility.

Example: ASP.NET Framework and the Hollywood Principle

In ASP.NET Web Forms, the Hollywood Principle is evident in the use of event-driven programming. Developers write code in response to lifecycle events (like Page_Load) and user actions (like Button_Click), but they do not control the flow of execution themselves. The ASP.NET framework handles request routing, lifecycle management, and event triggering.

ASP.NET Web Forms Example:

using System;

public partial class WebForm1 : System.Web.UI.Page
{
    // Respond to the Page_Load event
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            // Initial page load logic
            lblMessage.Text = "Welcome to the Hollywood Principle Example!";
        }
    }

    // Respond to a Button Click event
    protected void btnSubmit_Click(object sender, EventArgs e)
    {
        string name = txtName.Text;
        lblMessage.Text = $"Hello, {name}! This response is triggered by a button click.";
    }
}

Enter fullscreen mode Exit fullscreen mode

Key Points:

Framework-Controlled Flow:

  • The ASP.NET framework controls when Page_Loadand btnSubmit_Clickare called.
  • The developer doesn’t explicitly invoke these methods; they are wired to events managed by the framework.

Event Handlers:

  • The developer writes event handlers (Page_Load, btnSubmit_Click) that are triggered when specific events occur, like the page loading or a button being clicked

How do the major C# DI/IoC frameworks compare ?

In C#, Dependency Injection (DI) and Inversion of Control (IoC) are central concepts in modern application design, especially in frameworks built around ASP.NET Core and other enterprise applications. There are several DI/IoC frameworks available in the .NET ecosystem.

DI Containers Comparison

Here's a comparison of some popular Dependency Injection (DI) containers for C# and their use cases:

DI Container Best For Pros Cons
Microsoft.Extensions.DependencyInjection General-purpose apps, ASP.NET Core Lightweight, fast, easy to use, well-integrated with ASP.NET Core Lacks advanced features like interception, more complex configuration
Autofac Large applications, enterprise projects Powerful, flexible, supports advanced features like interception, decorators, fine-grained control Higher complexity, more performance overhead
Ninject Legacy systems, simple apps Easy to use, convention-based setup Outdated, slower performance
Castle Windsor Enterprise apps needing dynamic proxies and AOP Advanced features, flexible configuration Steep learning curve, slower performance
Simple Injector Performance-critical apps, smaller projects Fast, simple, easy to use Lacks advanced features like property injection
StructureMap Legacy systems needing flexible DI Flexible, supports conventions and lifecycle management Slow performance, outdated

Conclusion

  • Microsoft.Extensions.DependencyInjection is the default choice for most ASP.NET Core apps and general-purpose applications.
  • Autofac is ideal for enterprise-level applications that require advanced DI features like interception and fine-grained control.
  • Ninject is suitable for legacy systems or simpler apps but is now outdated.
  • Castle Windsor is powerful for enterprise apps needing dynamic proxies and AOP, but has a steep learning curve.
  • Simple Injector is great for performance-critical apps where simplicity and speed are key, though it lacks advanced features.
  • StructureMap is flexible but should mainly be considered for legacy systems, as it has become outdated and slower compared to others.

Top comments (0)