简体   繁体   中英

Generics with contravariant type parameters and Unity container

I have an interface with contravariant type parameter, say IFoo :

interface IFoo<in T> {}

Now I have three classes:

class B {}

class D : B {}

class Foo : IFoo<B> {}

If I register them like this

container.RegisterType<IFoo<B>, Foo>();

...then try to resolve IFoo<D> , it fails because I didn't really register IFoo<D> or IFoo<> . It's clear.

My current solution is just iterate over Registrations , find the registration which RegisteredType is assignable from resolving type ( IFoo<D> in my case) then resolve its MappedToType type.

The question is simple: is there a better way to do this? Any advice would be appreciated.

Thank you.

EDIT:

A little more context.

I have some kind of mapper.

IMapper<in TSource, in TDest> : IMapper // IMapper is non-generic
{
    void Map(TSource source, TDest dest);
}

And I want to register only base mapper (where TSource is IEnumerable<T> ) and be able to resolve this mapper for every type that implements IEnumerable<T> , for example for T[] :

object source = GetSource(); // runtime type is T[]
object dest = GetDest();

Type mapperType = typeof(IMapper<,>).MakeGenericType(source.GetType(), dest.GetType());
IMapper mapper = (IMapper) container.Resolve(mapperType);
mapper.Map(source, dest);

And yes, I'm interested only in Unity/C#-based approaches...

It sounds like you want to resolve the same Mapper every time. If that's right, and IEnumerable<> is the only interface that your mapper needs to deal with, then your approach seems like it's a bit overkill.

I would try registering the mapper using non-generic IEnumerable() , instead of IEnumerable<T> ( container.RegisterType<IFoo<IEnumerable>, Foo>(); ). If I remember correctly, a generic IEnumerable<T> can be passed to a non-generic IEnumerable() parameter, so you don't need to worry about what type parameter is passed. So when you go to resolve your mapper, you shouldn't even need to check the type, just resolve IMapper<IEnumerable> .

The following code compiled:

[TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethod1()
        {            
            IEnumerable<int> coll = new int[0];
            IEnumerable coll2 = coll;

         var blah = new Test<IEnumerable<int>>();
    }
}

public interface ITest<in T> where T : IEnumerable
{
}

public class Test<T> : ITest<T> where T : IEnumerable { }

If you need to get different implementations of IMapper , then that's another story. In that case, you have to register each implementation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM