Search code examples
c#genericscovariancenested-genericsinvariance

I've read all about Covariance, Contravariance and Invariance, but I still don't get how to design my code


I've searched and read/studied as much as seemed reasonable prior to posting this. I have found similar questions, but most posts actually relate more to passing "List of a derived types" to functions calls that require "List of a base type". I can appreciate the animal examples and feel like I have a much better grasp after my studies.

That being said, I still can't figure out how to solve in my particular use-case. I have a need to aggregate instances of a "GenericClass of TestInterface(s)" in a collection. I have copied/pasted below my best effort at what might seem like the best way to accomplish the tasks.

namespace Covariance
{
    class Program
    {

        protected static ISet<GenericClass<TestInterface>> set = new HashSet<GenericClass<TestInterface>>();

        static void Main(string[] args)
        {
            set.Add(new GenericClass<A>());
            set.Add(new GenericClass<B>());
        }
    }

    class GenericClass<TemplateClass> where TemplateClass : TestInterface
    {
        TemplateClass goo;
    }

    public interface TestInterface
    {
        void test();
    }
    public class A : TestInterface
    {
        public void test()
        {
        }
    }

    class B : A
    {
    }
}

The above code fails with the following compile errors:

error CS1503: Argument 1: cannot convert from 'Covariance.GenericClass' to 'Covariance.GenericClass'

error CS1503: Argument 1: cannot convert from 'Covariance.GenericClass' to 'Covariance.GenericClass'

Any help/guidance or relevant links would be much appreciated. Once again, my apologies if this is a duplicate question. Thank You!


Solution

  • You can only declare variance modifiers (in, out) on generic interfaces, not types. So one way to solve this problem would be to declare interface for your GenericClass, like this:

    interface IGenericClass<out TemplateClass> where TemplateClass : TestInterface {
        TemplateClass goo { get; }
    }
    class GenericClass<TemplateClass> : IGenericClass<TemplateClass> where TemplateClass : TestInterface
    {
        public TemplateClass goo { get; }
    }
    

    And then

    class Program {
        protected static ISet<IGenericClass<TestInterface>> set = new HashSet<IGenericClass<TestInterface>>();
    
        static void Main(string[] args) {
            set.Add(new GenericClass<A>());
            set.Add(new GenericClass<B>());
        }
    }