Search code examples
c#.net-coregeneric-type-argument

Someone can explain me why this error occurs I tried to create a provider to rosolver a list of service to make a connection from obj1 with service1


using System;
using System.Collections.Generic;

namespace ConsoleApp1
{
    public abstract class Provider
    {
        public abstract string Name { get; }
    }

    public abstract class Provider<T> : Provider
        where T : BaseService
    {
        public T Service { get; set; }
    }

    public class FooProvider : Provider<FooService>
    {
        public override string Name => "FooProvider";
    }

    public class BarProvider : Provider<BarService>
    {
        public override string Name => "BarProvider";
    }

    public abstract class BaseService
    {
        public abstract string Name { get; }
    }

    public class FooService : BaseService
    {
        public override string Name => "FooService";
    }

    public class BarService : BaseService
    {
        public override string Name => "BarService";
    }


    class Program
    {
        public static List<BaseService> Services = new List<BaseService> { };
        public static List<Provider<BaseService>> Providers = new List<Provider<BaseService>> { };

        static void Main()
        {
            Console.WriteLine("Services");
            Services.Add(new FooService());
            Services.Add(new BarService());
            Services.ForEach(service => Console.WriteLine(service.Name));

            Console.WriteLine("Provider");
            // Error CS1503  Argument 1: cannot convert from 'ConsoleApp1.FooProvider' to 'ConsoleApp1.Provider<ConsoleApp1.BaseService>'    ConsoleApp1 C:\Projetos\Git\sbh\src\ms\sbh - src - ms - migracao\ConsoleApp1\Program.cs   56  Active
            Providers.Add(new FooProvider());

            //Error CS1503  Argument 1: cannot convert from 'ConsoleApp1.BarProvider' to 'ConsoleApp1.Provider<ConsoleApp1.BaseService>'    ConsoleApp1 C:\Projetos\Git\sbh\src\ms\sbh - src - ms - migracao\ConsoleApp1\Program.cs   61  Active
            Providers.Add(new BarProvider());

            Providers.ForEach(provider => Console.WriteLine($"provider {provider.Name} - service {provider.Service.Name}"));
        }
    }
}

If you comment the command to add provider you'll see that errors not happens. I don't know explain why error occurs by I know that simple list of Service working and list of Provider not working.


Solution

  • This happens because of covariance in generics. The services in your example work because List<T> (or more precisely IEnumerable<T>) is marked as having a covariant parameter type.

    https://learn.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance#list-of-variant-generic-interface-and-delegate-types

    This allows a FooService to be used as BaseService. The generic in your Provider class is not marked with variance so it can't be used in the same way. If you want this functionality you need to create a variant generic interface.

    https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance/creating-variant-generic-interfaces