Search code examples
c#genericscovariancecontravariance

c# generics covariance and contravariance conflict


I have the following code written in c# using interfaces, inheritance and generics:

public interface IBasic
{

}

public class Basic : IBasic
{

}

public class AnotherBasic : Basic
{

}

public interface IWorker<in TBasic>
{
    void Run(TBasic basic);
}

public class Worker : IWorker<Basic>
{
    public void Run(Basic basic)
    {
        throw new System.NotImplementedException();
    }
}

public class AnotherWorker : IWorker<AnotherBasic>
{
    public void Run(AnotherBasic basic)
    {
        throw new System.NotImplementedException();
    }
}

public void Test()
{
    List<IWorker<IBasic>> workers = new List<IWorker<IBasic>>
    {
        new Worker(),
        new AnotherWorker()
    };
}

The problem of this code is that the worker and anotherworker classes do not fit in to the generics list of IWorker<IBasic> who are the parents of workers for both the worker and the basic class. The thing is that IWorker<in TBasic> is contravariant because of run method signature, however I need it covariant, in order to fill in the List<IWorker<IBasic>>. The run method has to have the TBasic parameter, and I need this list of workers on purpose of chain of responsibility design pattern. Do I miss something or did I find a reason to make covariance and contravariance not mutually exclusive ?


Solution

  • So after 2 days of learning and investigation I answered my own question. Here is the code:

    public interface IBasic {

    }
    
    public class Basic : IBasic
    {
    
    }
    
    public class AnotherBasic : Basic
    {
    
    }
    
    public interface IWorker<in TBasic>
    {
        void Run(TBasic basic);
    }
    
    public class SimpleWorker : IWorker<IBasic>
    {
        public void Run(IBasic basic)
        {
            throw new System.NotImplementedException();
        }
    }
    
    public class Worker : IWorker<Basic>
    {
        public void Run(Basic basic)
        {
            throw new System.NotImplementedException();
        }
    }
    
    public class AnotherWorker : IWorker<AnotherBasic>
    {
        public void Run(AnotherBasic basic)
        {
            throw new System.NotImplementedException();
        }
    }
    
    public class Final
    {
        public void Test()
        {
            List<IWorker<AnotherBasic>> workers = new List<IWorker<AnotherBasic>>
            {
                new SimpleWorker(),
                new Worker(),
                new AnotherWorker()
            };
        }
    }
    

    TBasic in CONTRAVARIANT, which means the declaration should be the most specific as possible, as shown in the code: AnotherBasic Then the types that are less derived, are the parents, are accepted, and the code compiles.