StructureMap and Nested Generics

2019-07-23 04:12发布

问题:

I am wondering if there is a way to wire up nested generics within StructureMap without having to specify the internal type or create type specific interfaces. I realize that this is a bit confusing, so a coding example might be a better explanation of the functionality that I am looking for.

public interface IParser<T> { }
public class Range<T> where T : struct { }
public class RangeParser<T> : IParser<Range<T>> { }

Theoretically, I would like to be able to follow StructureMap's open generic capabilities such as this:

For(typeof(IRepository<>)).Use(typeof(Repository<>)).

I realize that I cannot do this:

For(typeof(IParser<Range<>>).Use(typeof(RangeParser<>));

I have tried to do it via reflection's MakeGenericType, however, I seem to be running into issues with that approach.

var requestedType = typeof(IParser<>).MakeGenericType(typeof(Range<>));
For(requestedType).Use(typeof(RangeParser<>));

Any ideas would be greatly appreciated.

回答1:

StructureMap is quite limited when it comes to support for open generics. With Simple Injector on the other hand you can do the following:

// using SimpleInjector.Extensions;

container.RegisterOpenGeneric(typeof(IParser<>), typeof(RangeParser<>));

This will map IParser<Range<T>> to RangeParser<T> where T is a struct. If you have multiple generic implementations for IParser<T> you can simply add extra implementations:

container.RegisterOpenGeneric(typeof(IParser<>), typeof(RangeParser<>));
container.RegisterOpenGeneric(typeof(IParser<>), typeof(ReferenceTypeParser<>));

The only thing you need to make sure is that the registrations don't overlap. Simple Injector will throw an exception when you try to resolve (or inject) an closed IParser<T> type that can be handled by multiple registrations.

In case there is some overlap, you can supply a predicate to the RegisterOpenGeneric method like this:

container.RegisterOpenGeneric(typeof(IParser<>), typeof(RestrictiveParser<>));
container.RegisterOpenGeneric(typeof(IParser<>), typeof(FallbackParser<>), 
    c => !c.Handled);

These registrations are evaluated in order of registration.

You can use generic type constraints as natural filters and Simple Injector will follow them. If generic type constraints don't cut it, you can always register partial open generic types:

container.RegisterOpenGeneric(typeof(IParser<>), 
    typeof(SomeParser<>).MakeGenericType(typeof(List<>));

From all IoC frameworks for .NET, Simple Injector has the best support for generic typing:

  • It understands generic type constraints.
  • It allows complex nested generic type mappings.
  • It allows supplying partial open generic types.
  • It allows applying generic decorators to types.
  • It allows conditionally applying generic decorators based on their generic type constraints.
  • It allows registering collections with that mix non-generic implementations and auto-determined closed versions of supplied open generic types.