Search code examples
c#generics.net-coredependency-injection

Register C# Dep Injection for generic factory


I am trying to register a factory that can emit things, and do it so that I can request the most abstract version of this with DI and then decide at registration what combinations of things will actually be used.

The code:

// The thing

public interface IThing
{
    void DoSomething();
}

public class ThingA : IThing
{
    public void DoSomething() => throw new NotImplementedException();
}

public class ThingB : IThing
{
    public void DoSomething() => throw new NotImplementedException();
}

// The factory

public interface IThingFactory<TThing> where TThing : IThing
{
    TThing CreateThing();
}

public class MyThingFactory<TThing> : IThingFactory<TThing> where TThing : IThing
{
    public TThing CreateThing() => throw new NotImplementedException();
}

If I register and use these as follows, it works:

Services.AddSingleton<IThingFactory<ThingA>, MyThingFactory<ThingA>>();
...
var factory = ServiceProvider.GetRequiredService<IThingFactory<ThingA>>();
// factory is a MyThingFactory<ThingA>

What I really want to do is register things such that I can request a IThingFactory<IThing>, but for instance, the following definition fails to compile:

Services.AddSingleton<IThingFactory<IThing>, MyThingFactory<ThingA>>();

with

The type 'MyThingFactory' cannot be used as type parameter 'TImplementation' in the generic type or method 'ServiceCollectionServiceExtensions.AddSingleton<TService, TImplementation>(IServiceCollection)'. There is no implicit reference conversion from 'MyThingFactory' to 'IThingFactory'

It's not clear how to register this.


Solution

  • If you enable covariance on the interface using the out keyword you will be able to register the service.

    //                              +--- Add this
    //                              |
    public interface IThingFactory<out TThing> where TThing : IThing
    {
        TThing CreateThing();
    }
    

    Then you can implicitly cast from IThingFactory<ThingA> to IThingFactory<IThing>, which will allow registering the type. Like so:

    Services.AddSingleton<IThingFactory<IThing>, MyThingFactory<ThingA>>();