Introduction
I don’t need to tell you that IoC and an IoC-container is something you should use. My container of preference is StructureMap by Jeremy D. Miller.
One of the more interesting things to map are generic interfaces and their implementations. Generics can save us a lot of typing and make life a lot easier. Less code usually means less bugs. But there are also 2 different ways in which generic interfaces can be implemented – one is open and the other one is more specific.
Prerequisites
I used.
- StructureMap 2.6.1
- .Net 4.0
- VB 10
- C# 4.0
- VS2010
StructureMap 2.6.1 won’t work with the .Net framework 4.0 client profile. It will give you the following warning (I wonder why that is not an error since it will no longer compile)
The referenced assembly “StructureMap” could not be resolved because it has a dependency on “System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a” which is not in the currently targeted framework “.NETFramework,Version=v4.0,Profile=Client”. Please remove references to assemblies not in the targeted framework or consider retargeting your project.
The code
Let’s say we have an interface like this.
Public Interface IGenericInterface(Of T)
Function ReturnString(ByVal value As T)
End Interface
And for the less lucky among us.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
public interface IGenericInterface<T>
{
String ReturnString(T value);
}
}
We can have 2 different ways to implement this interface.
The more generic one ;-).
Public Class GenericClass(Of T)
Implements IGenericInterface(Of T)
Public Function ReturnString(ByVal value As T) As Object Implements IGenericInterface(Of T).ReturnString
Return value.ToString
End Function
End Class```
And in C#.
```csharp
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
public class GenericClass<T>: IGenericInterface<T>
{
public String ReturnString(T value)
{
return value.ToString();
}
}
}
And a more specific implementation.
Public Class GenericClass2
Implements IGenericInterface(Of String)
Public Function ReturnString(ByVal value As String) As Object Implements IGenericInterface(Of String).ReturnString
Return value & "2"
End Function
End Class
And in C#.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
public class GenericClass2 : IGenericInterface<String>
{
public String ReturnString(String value)
{
return value + "2";
}
}
}
Now lets see if we can get these things mapped.
Imports StructureMap
Module Module1
Sub Main()
Dim _container As New Container()
_container.Configure(Sub(x)
x.For(GetType(IGenericInterface(Of ))).Use(GetType(GenericClass(Of )))
End Sub)
Console.WriteLine(_container.GetInstance(Of IGenericInterface(Of String)).ReturnString("test"))
Console.WriteLine(_container.GetInstance(Of IGenericInterface(Of Integer)).ReturnString(1))
Console.ReadLine()
End Sub
End Module
If we run the above then we will get
test
1
So we are getting GenericClass as a return and not Genericlass2 for the first Console.Writeline.
But we can make it return the more specific implementation like so.
Imports StructureMap
Module Module1
Sub Main()
Dim _container As New Container()
_container.Configure(Sub(x)
x.For(Of IGenericInterface(Of String)).Use(Of GenericClass2)()
x.For(GetType(IGenericInterface(Of ))).Use(GetType(GenericClass(Of )))
End Sub)
Console.WriteLine(_container.GetInstance(Of IGenericInterface(Of String)).ReturnString("test"))
Console.WriteLine(_container.GetInstance(Of IGenericInterface(Of Integer)).ReturnString(1))
Console.ReadLine()
End Sub
End Module
Now we get the following result.
test2
1
So it is picking the more specific implementation if it can.
This is the C# version.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using StructureMap;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Container _container = new Container();
_container.Configure(x => {
x.For<IGenericInterface<String>>().Use<GenericClass2>();
x.For(typeof (IGenericInterface<>)).Use(typeof (GenericClass<>));
}
);
Console.WriteLine(_container.GetInstance<IGenericInterface<String>>().ReturnString("test"));
Console.WriteLine(_container.GetInstance<IGenericInterface<int>>().ReturnString(1));
Console.ReadLine();
}
}
}
Conclusion
Mapping and resolving generic types is very easy with StructureMap. You have no reason whatsoever not to use them and not to use StructureMap to resolve them.