Generic registration question

Nov 15, 2010 at 6:45 PM

Given the following interfaces:

public interface IFoo
{
     // Marker only
}

public interface IFooHandler<T> where T: IFoo
{
     void Handle(T foo);
}

I want to scan some assemblies for all classes that implement IFooHandler<T> and register them for the specific T they implement. Then, at some point in my application code, I want to resolve all IFooHandlers for whatever T I'm dealing with. This has me stumped.

If I wasn't trying to auto-register all types, I could do this easily with:

container.RegisterType(typeof(IFooHandler<Cat>), typeof(NormalCatFooHandler));

container.RegisterType(typeof(IFooHandler<Cat>), typeof(SpecialCatFooHandler));


What I need to do is to scan the assembly, get back types of NormalCatFooHandler, and SpecialCatFooHandler and then register them to hande IFooHandler<Cat>. Assuming I'm able to handle the scanning of assemblies, how would I do the registration?

Thanks

Nov 15, 2010 at 9:04 PM

You'll need to dive into the reflection API - or more specifically, the Type.MakeGenericType method. This takes an open generic type (IFooHandler<T>), and generates a closed generic (IFooHandler<Cat>) based on the type arguments provided.

You'd end up doing something like this:

 

var handlerType = typeof(IFooHandler<>).MakeGenericType(targetType);
container.RegisterType(handlerType, implementationType);

Nov 15, 2010 at 9:28 PM
Edited Nov 15, 2010 at 9:31 PM

I figured I'd have to use the MakeGenericType for that purpose. What I'm still unsure of is how to get the generic type used by a closed generic.

Currently I'm scanning the assemblies for all types that implement IFooHandler<>. Once I have those types, how do I determine the target type?

Here's some code I'm using:

 

// Scan the assemblies for all types that implement IFooHandler<>
var handlers = (from type in _buildManager.ConcreteTypes
                from @interface in type.GetInterfaces()
                where
                   @interface.IsGenericType &&
                   @interface.GetGenericTypeDefinition() == typeof (IFooHandler<>)
                select type).ToArray();


 

Now I want to loop through each handler type in handlers and register the type in the container.

 

foreach(var handler in handlers)
{
     var targetType = ? // Not sure how to get this from handler
     var handlerType = typeof(IFooHandler<>).MakeGenericType(targetType);
     container.RegisterType(handlerType, handler);
}

 

Nov 16, 2010 at 5:31 AM

Ahh, ok, that's pretty easy. Given a closed generic type, you can call Type.GetGenericTypeArguments and you'll get back an array of Type objects. In this case, there's only going to be one, so:


var targetType = handler.GetGenericArguments()[0];

should do the trick. Although I'd probably rethink the names a little bit - naming it "handler" and "handlers" when you're actually dealing with the types rather than instances is a little confusing to me.

 

 

Nov 16, 2010 at 6:07 PM
Edited Nov 16, 2010 at 7:10 PM
ctavares wrote:

var
targetType = handler.GetGenericArguments()[0];

Thanks for the help, but this doesn't actually work though, giving me an invalid index exception. I don't think this works because the handler type doesn't actually contain any generic arguments itself. Instead, it implements an interface with generics.

I found a method of solving my problem by reversing the logic and looking for IFoo types first, but it's less efficient and I'd like to get it working as stated here.  Below is what a handler in my system would look like, and is the "handler" in the above code. "handler" would be what you get when you do typeof(SampleHandler). I need to now figure out exactly what IFoo is being used ("Bar" in this case).

 

public class SampleHandler : IFooHandler<Bar>
{
     // Implementation
}
public class Bar : IFoo
{...}

 

Nov 17, 2010 at 6:26 PM

Ah, ok. Actually, it will work, but you need to call the GetGenericArguments method on the type of the interface it implements then. I got a little wrapped around the names. So try this:

foreach(var handlerInterface in from @interface in
    type.GetInterfaces() where 
    @interface.IsGenericType &&
    @interface.GetGenericTypeDefinition() == typeof (IFooHandler<>))
{
    var targetType = handlerInterface.GetGenericArguments()[0];
    // do whatever
}