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 Generic Interfaces and Classes
  • Registering Type Mappings For Generics
  • Registering Generic Arrays
  • Support for Generic Decorator Chains
  • Methods for Registering Generic Parameters and Types
  • More Information

Registering Generic Interfaces and Classes

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<>));
'Usage
container.RegisterType(GetType(IGenericClass(Of )), GetType(GenericClass(Of )))

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;
    }
}
'Usage
Public Class MyClass1(Of T)
    Public InjectedValue As T
    Public Sub New(s As String, o As Object)
    End Sub

    Public Sub New(injectedValue As T)
        InjectedValue = injectedValue
    End Sub
End Class

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")));
'Usage
' Where MyClass1 has at least one generic parameter
container.RegisterType(GetType(MyClass1(Of )), _
                       New InjectionConstructor("Name", _
                       New InjectionParameter(Of 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);
'Usage
' Where MyClass1 has at least one generic parameter
' Inject constructor with argument GenericParameter.
container.RegisterType(GetType(MyClass1(Of )), _
                       New InjectionConstructor(New GenericParameter("T")))
Dim a As New Account()
container.RegisterInstance(Of 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);
'Usage
' Where MyClass1 has at least one generic parameter.
' Inject constructor with argument.
container.RegisterType(GetType(MyClass1(Of )), _
          New InjectionConstructor(New GenericParameter("T", "named")))
'Create Account instances and register using registerInstance
Dim a As New Account()
container.RegisterInstance(Of Account)(a)
Dim named As New Account()
container.RegisterInstance(Of Account)("named", named)

Note

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.

Registering Type Mappings for Generics

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");
'Usage
' Map the open generic interface IOpenGenericInterface to the 
' open class MyOpenClass.
Public Class MyOpenClass(repositoryAs IOpenGenericInterface(Of T), _
                         validator As IValidator(Of T))
  ...
End Class
Public Class MyOpenClass(Of T)
    Implements IOpenGenericInterface(Of T)
    ...
End Class
' Map the open generic interface IOpenGenericInterface
' to the open class MyOpenClass with the name "validating".
' Use the GetType keyword to specify Type instances.
container.RegisterType(GetType(IOpenGenericInterface(Of )), _
                       GetType(MyOpenClass(Of )), _
                       "validating")

Note

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>();
'Usage
container.RegisterType(Of IValidator(Of StockQuote), _
                       RandomStockQuoteValidator)()

Registering Generic Arrays

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; }
    }
}
'Usage
Public Class MyClass2(Of T)
    Public injectedValue As T()
    Public ReadOnly DefaultConstructorCalled As Boolean

    Public Sub New()
        DefaultConstructorCalled = True
    End Sub

    Public Sub New(injectedValue As T())
        DefaultConstructorCalled = False

        Me.injectedValue = injectedValue
    End Sub

    Public Property InjectedValue() As T()
        Get
            Return Me.injectedValue
        End Get
        Set
            Me.injectedValue = value
        End Set
    End Property
End Class

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")));
'Usage
Dim container As IUnityContainer = New UnityContainer() _
    .RegisterType( _
         GetType(MyClass2(Of )), _
         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);
'Usage
Dim a0 As New Account()
container.RegisterInstance(Of Account)("a0", a0)
Dim a1 As New Account()
container.RegisterInstance(Of Account)("a1", a1)
Dim a2 As New Account()
container.RegisterInstance(Of Account)(a2)

You resolve the array as follows:

MyClass2<Account> result = container.Resolve<MyClass2<Account>>();
'Usage
Dim result As MyClass2(Of Account) = container.Resolve(Of MyClass2(Of 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"))));
'Usage
Dim container As IUnityContainer = New UnityContainer() _
      .RegisterType(GetType(MyClass2(Of )), _
           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")));
'Usage
Dim container As IUnityContainer = New UnityContainer() _
    .RegisterType(GetType(MyClass2(Of )), _
        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;
    }
}
'Usage
Public Class SupportClass
End Class

Public Class ClassGeneric(Of T)

    Private m_arrayProperty As T()
    Public Property ArrayProperty() As T()
        Get
            Return m_arrayProperty
        End Get
        Set(ByVal value As T())
            m_arrayProperty = value
        End Set
    End Property
    
    Private m_arrayCtor As T()
    Public Property ArrayCtor() As T()
        Get
            Return m_arrayCtor
        End Get
        Set(ByVal value As T())
            m_arrayCtor = value
        End Set
    End Property
    
    Public Sub New()
    End Sub
    
    ' A generic class with an generic array property
    Public Sub New(ByVal arrayCtor__1 As T())
        ArrayCtor = arrayCtor__1
    End Sub

End Class

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")));
}
'Usage
Public  Sub ConfigureGenericInjection()
  Dim container As IUnityContainer = New UnityContainer() _
    .RegisterType(GetType(ClassGeneric(Of )), _
                  New InjectionProperty("ArrayProperty", _
                  New GenericResolvedArrayParameter("T")))
End Sub

Methods for Registering Generic Parameters and Types

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.

More Information

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