2

Consider the following classes and interfaces:

interface IFactory<T>{}
class Factory<T> : IFactory<T>{ }

interface IEntity{}
class Entity : IEntity{ }

I would like Autofac to resolve IFactory<IEntity> to Factory<Entity> like this:

b.RegisterType<Factory<Entity>>().As<IFactory<IEntity>>();

But I get the following exception (abbreviated for clarity):

The type 'Factory`1[Entity]' is not assignable to service 'IFactory`1[[IEntity]]'

Why is that and how can the issue be resolved? Or am I trying something "wrong"?

I briefly looked into RegisterGeneric, but I don't think it applies here; also, because the above is just an example. In other cases I may want to define a different component for IFactory<IEntity>.

mike
  • 1,627
  • 1
  • 14
  • 37
  • What happens when you add this line of code to your app `IFactory x = new Factory();`? It won't work right? – DavidG May 11 '18 at 10:18

1 Answers1

5

This isn't an Autofac issue - it's a matter of generic variance. You'd see exactly the same in a simple C# program:

public class Program 
{
    public static void Main() 
    {
        IFactory<IEntity> factory = new Factory<Entity>();
    }
}

Most generic interfaces are invariant - the type arguments have to match exactly. Some are covariant like IEnumerable<T>, allowing you to write:

IEnumerable<object> objects = new List<string>();

and some are contravariant like IComparer<T>, allowing you to write:

IComparer<string> = Comparer<object>.Default;

Covariant interfaces only allow their type parameters to come "out" of the implementation (e.g. via return types). Contravariant interfaces only allow their type parameters to go "into" the implementation (e.g. via regular parameters). It gets subtle when you have delegate parameters that themselves accept values etc, but we'll ignore that for now...

It sounds like your IFactory<T> should be covariant - so you just change the declaration like this:

interface IFactory<out T>{}

At that point, the code compiles and I'd hope that Autofac can handle it too. That does require that your interface never uses T as an input though. (We can't tell as you haven't shown any interface members.)

For more details on generic variance, see the MS documentation.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    This is assuming that `T` isn't used contravariantly of course. – DavidG May 11 '18 at 10:22
  • 1
    @DavidG: Indeed. The name "factory" would suggest that, but we can't tell without more details. – Jon Skeet May 11 '18 at 10:22
  • 1
    Exactly. Good answer though. type variance comes up on here so often... – DavidG May 11 '18 at 10:24
  • 1
    I always forgot _comparer_ as a contravariant example. And i always revisit the following question: https://stackoverflow.com/questions/6434207/benefits-of-contravariance-in-icomparer-iequalitycomparer-interfaces for good examples in a explain-me-like-im-five manner. – Razvan Dumitru May 11 '18 at 10:33