4

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!

Mong Zhu
  • 23,309
  • 10
  • 44
  • 76
  • How are you expecting to use the `set`? How are you going to distinguish between `set[0]` and `set[1]` items? You first need to have clear idea of what you need your code to do, before you start going crazy with interfaces, inheritance and generics. – Euphoric Nov 23 '16 at 08:14
  • It's been long since I've programmed C#, but back then, I think, generic classes were always invariant, and only interfaces with parameters specified as `in` or `out` had variance. Which seems to be the the problem here: `GenericClass` is not comparable to `GenericClass` or `GenericClass` at all. – phipsgabler Nov 23 '16 at 08:18

1 Answers1

4

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>());
    }
}
Evk
  • 98,527
  • 8
  • 141
  • 191