Channels ▼
RSS

C/C++

Dependency Injection


There are several principal patterns in dependency injection, the most common of which is constructor injection. A less common but useful pattern is method injection, which I discuss in this article. When a dependency can vary with each method call, you can supply it via a method parameter, which is the basis of method injection.

injection
Figure 1: A Client creates an instance of SomeClass, but first injects an instance of the dependency ISomeInterface with each method call.

How It Works

The caller supplies the dependency as a method parameter in each method call. It can be as simple as this method signature: 

public void DoStuff(ISomeInterface dependency)

Often, the dependency will represent some sort of context for an operation that's supplied alongside a "proper" value:

public string DoStuff(SomeValue value, ISomeContext context)

In this case, the value parameter represents the value on which the method is supposed to operate, whereas the context contains information about the current context of the operation. The caller supplies the dependency to the method, and the method uses or ignores the dependency as it best suits it.

If the service uses the dependency, it should be sure to test for null references first, as shown in the following code.

public string DoStuff(SomeValue value, ISomeContext context)
{
    if (context == null)
    {
        throw new ArgumentNullException("context");
    }
    return context.Name;
}

The guard clause guarantees that the context is available to the rest of the method body. In this example, the method uses the context's name to return a value.

If a method doesn't use the supplied dependency, it doesn't need to contain a guard clause. This sounds like a strange situation, but you may need to keep it if the method is part of an interface implementation.

When To Use Method Injection

Method injection is best used when the dependency can vary with each method call. This can be the case when the dependency itself represents a value, but is often seen when the caller wishes to provide the consumer with information about the context in which the operation is being invoked.

This is often the case in add-in scenarios where an add-in is provided with information about the runtime context via a method parameter. In such cases, the add-in is required to implement an interface that defines the injecting methods.

Imagine an add-in interface with this structure:

public interface IAddIn
{
    string DoStuff(SomeValue value, ISomeContext context);
}

Any class implementing this interface can be used as an add-in. Some classes may not care about the context at all, whereas other implementations will. A client may use a list of add-ins by calling each with a value and a context to return an aggregated result:

public SomeValue DoStuff(SomeValue value)
{
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }

    var returnValue = new SomeValue();
    returnValue.Message = value.Message;
    
    foreach (var addIn in this.addIns)
    {
        returnValue.Message =
        addIn.DoStuff(returnValue, this.context);
    }

    return returnValue;
}

The private addIns field is a list of IAddIn instances, which allows the client to loop through the list to invoke each add-in's DoStuff method. Each time the DoStuff method is invoked on an add-in, the operation's context represented by the context field is passed as a method parameter.

Method injection is closely related to the use of Abstract Factories. Any Abstract Factory that takes an abstraction as input can be viewed as a variation of method injection.

At times, the value and the operational context are encapsulated in a single abstraction that works as a combination of both. Method injection is different from other types of dependency-injection patterns in that the injection doesn't happen in a composition root, but rather, dynamically at invocation time. This allows the caller to provide operation-specific context, which is a common extensibility mechanism used in the .NET BCL.

Known Use

The .NET BCL provides many examples of method injection, particularly in the System.ComponentModel namespace. System.ComponentModel.Design.IDesigner is used for implementing custom design-time functionality for components. It has an Initialize method that takes an IComponent instance so that it knows which component it's currently helping to design. Designers are created by IDesignerHost implementations that also take IComponent instances as parameters to create designers:

IDesigner GetDesigner(IComponent component);

This is a good example of a scenario where the parameter itself carries information: The component may carry information about which IDesigner to create, but at the same time, it's also the component upon which the designer must subsequently operate.

Another example in the System.ComponentModel namespace is the TypeConverter class. Several of its methods take an instance of ITypeDescriptorContext that, as the name says, conveys information about the context of the current operation. Because there are many such methods, I won't list them all, but here is a representative example:

public virtual object ConvertTo(ITypeDescriptorContext context, 
                                CultureInfo culture, 
                                object value, 
                                Type destinationType)

In this method, the context of the operation is communicated explicitly by the context parameter, while the value to be converted and the destination type are sent as separate parameters. Implementers can use or ignore the context parameter as they see fit.

ASP.NET MVC also contains several examples of method injection. The IModelBinder interface can be used to convert HTTP GET or POST data into strongly typed objects. Its only method is:

object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext);

In the BindModel method, the controllerContext parameter contains information about the operation's context (among other things, the HttpContext), whereas the bindingContext carries more explicit information about the values received from the browser.

If you're building a framework, method injection can often be useful, because it allows you to pass information about the context to add-ins to the framework. That's one reason why we see method injection used so prolifically in the BCL.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video