TryHard
TryHard

Reputation: 135

Can't convert interface to concrete interface

Why i can't convert implementation of interface which concrete implement generic interface? I need for Cat, Dog etc own interface realisation.

public interface IMarker { }
public class ResultA : IMarker
{
}
public class ResultB : IMarker
{ }

public interface IService<T> where T : IMarker
{
    public List<T> DoStuff();
}

public interface ICatService : IService<ResultA>
{ }
public interface IDogService : IService<ResultB>
{ }

public class CatService : ICatService
{
    public List<ResultA> DoStuff()
    {
        return new List<ResultA>();
    }
}

public class DogService : IDogService
{
    public List<ResultB> DoStuff()
    {
        return new List<ResultB>();
    }
}

public abstract class Animal
{
    protected readonly IService<IMarker> _svc;

    protected Animal(IService<IMarker> svc)
    {
        _svc = svc;
    }
}
public class Cat : Animal
{
    public Cat(ICatService svc) : base(svc)
    {
    }
}

public class Dog : Animal
{
    public Dog(ICatService svc) : base(svc)
    {
    }
}

CS1503 Argument 2: cannot convert from 'ICatService' to 'IService'

I have DI for services i.e. :

services.AddTransient<ICatService, CatService>();

Upvotes: 1

Views: 69

Answers (1)

Guru Stron
Guru Stron

Reputation: 141600

The reason for such behaviour is that in general case IService<ResultA> is not IService<IMarker> (basically I would argue the same because C# classes do not support variance, for pretty good reason - see more here and here).

In this concrete case everything can be fixed by making the interface covariant and leveraging the covariance of IEnumerable<T>:

public interface IService<out T> where T : IMarker
{
    public IEnumerable<T> DoStuff();
}

public class CatService : ICatService
{
    public IEnumerable<ResultA> DoStuff() => return new List<ResultA>();
}

public class Cat : Animal
{
    public Cat(CatService svc) : base(svc)
    {
    }
}

But not sure that in your actual code you will be able to.

Or just make the base class generic (if this suits your use case):

public abstract class Animal<T> where T : IMarker
{
    protected readonly IService<T> _svc;

    protected Animal(IService<T> svc)
    {
        _svc = svc;
    }
}

Original answer

CatService does not implement ICatService, i.e. the fact that ICatService inherits only IService<ResultA> does not mean that they are the same, C# is strongly-typed (mostly :-) language and compiler will consider those two interfaces being different ones (though related). You need either to make CatService to implement ICatService:

public class CatService : ICatService
{
    // ...
}

Or register and resolve the IService<ResultA> interface (basically skipping intermediate interface at all):

services.AddTransient<IService<ResultA>, CatService>();

// ...

public Cat(IService<ResultA> svc) : base(svc){}

Upvotes: 3

Related Questions