Search code examples
c#inheritanceinterfacecovariancecontravariance

Cannot assign a derived class to its parent generic interface


I am trying to assign a class that inherits from a derived class, but the c# compiler will not let me.

This is an example setup of what I am trying to achieve.

using System;

namespace ConsoleApp
{
    interface IInterfaceA
    {
    }

    interface IInterfaceB
    {

    }

    class TypeA : IInterfaceA
    {

    }

    class TypeB : IInterfaceB
    {

    }


    interface IMapper<in TIn, out TOut>
        where TIn : IInterfaceA
        where TOut :IInterfaceB
    {
        TOut MapAToB(TIn input);
    }

    class AToBMapper : IMapper<TypeA, TypeB>
    {
        public TypeB MapAToB(TypeA input) => throw new NotImplementedException();
    }



    class Program
    {
        static void Main(string[] args)
        {
            IMapper<IInterfaceA, IInterfaceB> test = new AToBMapper();
        }
    }
}

Everything works fine, until I reach the following line.

IMapper<IInterfaceA, IInterfaceB> test = new AToBMapper();

In it, the C# compiler fails with an error, telling me that

Cannot implicitly convert type 'ConsoleApp.AToBMapper' to 'ConsoleApp.IMapper<ConsoleApp.IInterfaceA, ConsoleApp.IInterfaceB>'. An explicit conversion exists (are you missing a cast?)

However, I cannot seem to understand why. The generic parameters of the IMapper interface are marked to be covariant and contravariant respectively, and my AtoBMapper inherits from the ITypeMapper.

Shouldn't this be possible, given that TypeA is an IInterfaceA and TypeB is an IInterfaceB ?


Solution

  • It works perfect, if you are assigning to IMapper(TypeA,TypeB), or if you are implementing the AToBMapper as IMapper(IInterfaceA, IInterfaceB).

    What you are doing is to implement the mapper on a concrete class implementation (TypeA, TypeB), not on the generic interfaces for these classes. Your implementation might therefore use more concrete definitions to do the mapping, as the interface allows.

    Using you implementation you could do something like defining a TypeC class, implementing interface IInterfaceA. This class would fullfill the interface IInterfaceA and would be allowed to be used on an IMapper(IInterfaceA, IInterfaceB). But the actual implementation would expect a TypeA class, not a TypeC class.