Registering Generic Parameters and Types

This topic explains how you can register the information required for injection for generic types, including generic arrays. You can specify a generic type when you register a type in the Unity container in almost exactly the same way as you register non-generic types. Unity provides two classes specifically for registering generics, GenericParameter for specifying that an instance of a generic type parameter should be resolved, and GenericResolvedArrayParameter for specifying that an array containing the registered instances of a generic type parameter should be resolved.

See the "Specifying Types in the Configuration File" section in the Specifying Types in the Configuration File topic for more details on generics, including a discussion of unbounded, closed, and open generic types.

This topic contains the following sections that explain registering generics:

Registering closed generic types works exactly like it does for non-generic types. For more information see Registering Types and Type Mappings, Creating Instance Registrations and Registering Injected Parameter and Property Values.

You can use the RegisterInstance method to register open generic types, types with all their generic type parameters left unspecified, with non-ambiguous constructors.

Unity generic parameter injection run time API configuration support for parameters and properties is provided by the GenericParameter class. Use the RegisterType overloads with GenericParameter to register generic types. The following example registers the open generic interface, IGenericClass, in the container as the registered type, and registers GenericClass as the target type to be returned in response to a query for IGenericClass. Due to the syntax limitations in C# and Visual Basic .NET, you must use the overloads of RegisterType that take explicit Type objects instead of the generic version of RegisterType.

container.RegisterType(typeof(IGenericClass<>), typeof(GenericClass<>));

The following example class taking a generic parameter is used in the generic type registration examples.

public class MyClass1<T>
{
    public T InjectedValue;
    public MyClass1 (string s, object o)
    {
    }

    public MyClass1 (T injectedValue)
    {
         InjectedValue = injectedValue;
    }
}

The following examples show the registration for open generic types and type mappings using RegisterType with a generic parameter.

Here is how to call a non-generic constructor on an open generic type.

// Where MyClass1 has at least one generic parameter
container.RegisterType(typeof (MyClass1<>),
                       new InjectionConstructor("Name", 
                       new InjectionParameter<object>("objectName")));

Here is how to call a constructor that takes a generic parameter. Here we designate the generic parameter by using GenericParameter.

// Where MyClass1 has at least one generic parameter
// Inject constructor with argument GenericParameter.
container.RegisterType(typeof (MyClass1<>),
                       new InjectionConstructor(new GenericParameter("T")));
Account a = new Account();
container.RegisterInstance<Account>(a);

Here we configure a named resolution of GenericParameter() and designate the generic parameter by using GenericParameter.

// Where MyClass1 has at least one generic parameter.
// Inject constructor with argument.
container.RegisterType(typeof(MyClass1<>),
          new InjectionConstructor(new GenericParameter("T", "named")));
//Create Account instances and register using registerInstance
Account a = new Account();
container.RegisterInstance<Account>(a);
Account named = new Account();
container.RegisterInstance<Account>("named", named);

Ff660936.note(en-us,PandP.20).gifNote:
If you need to specify a mapping—for example, if an instance is registered to supply the implementation for an interface, the generic type argument must be provided with the RegisterInstance method.

You can use the RegisterType method to register mappings in the container that include generics. The following example maps an open generic interface, IOpenGenericInterface<,>, to an open class, MyOpenClass<,>, with the validating name.

// Map the open generic interface IOpenGenericInterface to the 
// open class MyOpenClass.
public MyOpenClass(IOpenGenericInterface<T> repository, 
                   IValidator<T> validator)
{
  ...
}
public class MyOpenClass<T> : IOpenGenericInterface<T>
{
    …
}

// Map the open generic interface IOpenGenericInterface
// to the open class MyOpenClass with the name "validating".
// Use the typeof keyword to specify Type instances.
container.RegisterType(typeof(IOpenGenericInterface<>),
                       typeof(MyOpenClass<>),
                       "validating");

Ff660936.note(en-us,PandP.20).gifNote:
Open generic types cannot be used as generic type arguments. The version of the RegisterType method taking Type instances as parameters is used instead of the version with generic type parameters. The version with generic type parameters benefits from compile-time type checks.


The following example maps a closed generic interface to a non-generic class by using the RegisterType method to map the closed IValidator<StockQuote> interface to the non-generic RandomStockQuoteValidator. The RandomStockQuoteValidator class is a non-generic class that implements the closed generic interface, IValidator<StockQuote>.

container.RegisterType<IValidator<StockQuote>, RandomStockQuoteValidator>();

Unity provides the GenericResolvedArrayParameter to enable you to specify that an array containing the registered instances of a generic type parameter should be resolved. You can specify the entire array or specific named instances.

The following examples use the MyClass2 class. MyClass2 has a constructor with a generic array parameter.

public class MyClass2<T>
{
    public T[] injectedValue;
    public readonly bool DefaultConstructorCalled;

    public MyClass2()
    {
        DefaultConstructorCalled = true;
    }

    public MyClass2(T[] injectedValue)
    {
        DefaultConstructorCalled = false;

        this.injectedValue = injectedValue;
    }

    public T[] InjectedValue
    {
        get { return this.injectedValue; }
        set { this.injectedValue = value; }
    }
}

The following example registers a generic array by using the RegisterType method with a GenericResolvedArrayParameter parameter.

Here we call a constructor by using constructor injection, InjectionConstructor, that takes a generic array, as a parameter, new GenericResolvedArrayParameter("T").

IUnityContainer container = new UnityContainer()
    .RegisterType(
         typeof(MyClass2<>),
         new InjectionConstructor(new GenericResolvedArrayParameter("T")));

Then register three named instances, a0, a1 and a3, for the type Account:

Account a0 = new Account();
container.RegisterInstance<Account>("a0", a0);
Account a1 = new Account();
container.RegisterInstance<Account>("a1", a1);
Account a2 = new Account();
container.RegisterInstance<Account>(a2);

You resolve the array as follows:

MyClass2<Account> result = container.Resolve<MyClass2<Account>>();

Then specify the named instances to resolve. The InjectionConstructor argument is again supplied by new GenericResolvedArrayParameter but has its arguments supplied by new GenericParameter("T", "a2") that returns a named instance.

IUnityContainer container = new UnityContainer()
      .RegisterType(
                  typeof(MyClass2<>),
                  new InjectionConstructor(
                      new GenericResolvedArrayParameter(
                            "T",
                            new GenericParameter("T", "a2"),
                            new GenericParameter("T", "a1"))));

Now, set a property with a generic parameter array type. Property injection for MyClass2 is specified by InjectionProperty, which injects the property named InjectedValue with the generic array returned by new GenericResolvedArrayParameter("T").

IUnityContainer container = new UnityContainer()
    .RegisterType(typeof(MyClass2<>),
        new InjectionConstructor(),
        new InjectionProperty("InjectedValue", 
                               new GenericResolvedArrayParameter("T")));

Support for Generic Decorator Chains

Support for generic decorator chains is provided by the generic parameter. The following example uses a generic decorator with a generic class and a generic array parameter.

public class SupportClass
{ }

public class ClassGeneric<T>
{
    private T[] arrayProperty;
    public T[] ArrayProperty
    {
        get { return arrayProperty; }
        set { arrayProperty = value; }
    }

    private T[] arrayCtor;
    public T[] ArrayCtor
    {
        get { return arrayCtor; }
        set { arrayCtor = value; }
    }

    public ClassGeneric()
    { }

    // A generic class with an generic array property
    public ClassGeneric(T[] arrayCtor)
    {
        ArrayCtor = arrayCtor;
    }
}

The following example programmatically configures property injection. The RegisterType method registers a property with the name ArrayProperty, with ClassGeneric. InjectionProperty provides the property name and value. The name of the property is specified by the first parameter for InjectionProperty and the value for the property is specified by the second parameter, GenericResolvedArrayParameter("T").

public void ConfigureGenericInjection()
{
  IUnityContainer container = new UnityContainer()
    .RegisterType(typeof(ClassGeneric<>),
                  new InjectionProperty("ArrayProperty", 
                  new GenericResolvedArrayParameter("T")));
}

The following table summarizes the methods you can use to register generic parameters and types with the container at run time.

Method

Description

GenericResolvedArrayParameter(string genericParameterName, params object[] elementValues)

Creates a new GenericResolvedArrayParameter instance that specifies that the given named generic parameter should be resolved where genericParameterName is the generic parameter name to resolve and elementValues represents the values for the elements that will be converted to InjectionParameterValue objects.

GenericParameter(string genericParameterName)

Creates a new GenericParameter instance that specifies that the given named generic parameter should be resolved where

genericParameterName is the generic parameter name to resolve.

GenericParameter(string genericParameterName, string resolutionKey)

Creates a new GenericParameter instance that specifies that the given named generic parameter should be resolved where

genericParameterName is the generic parameter name to resolve and resolutionKey is the name to use when looking up the value in the container.

For more information about the techniques discussed in this topic, see the following topics:


Show: