6

I was reading a bit on generic variance and I don't have a full understanding of it yet but I'd like to know if it makes something like the following possible?

class A<T> { }

class B { }

class C : B { }

class My1  {
    public My1(A<B> lessDerivedTemplateParameter)
    {
    }
}

class My2 : My1 {
    public My2(A<C> moreDerivedTemplateParameter)
        : base(moreDerivedTemplateParameter) // <-- compile error here, cannot convert
    {
    }
}
Aaron Anodide
  • 16,906
  • 15
  • 62
  • 121
  • 1
    I am not as fluent in the generic variance as i would like to be, hence this a comment, not an answer, but i think you need to define `A` as `A` to make this compile. – psubsee2003 May 25 '12 at 19:17
  • 3
    @psubsee2003: I believe that is only allowed for interfaces. – Douglas May 25 '12 at 19:20
  • interesting, i did your suggestion plus had to make A and interface, then it compiles...i'll keep going and see if I can get the same result I was after, thanks... – Aaron Anodide May 25 '12 at 19:21
  • Generic variance does not work with classes in C#. If `A` were an interface, then you can declare it as `A`, and then you could call the base constructor the way you want. – dlev May 25 '12 at 19:21
  • @Douglas thanks, taught me something too – psubsee2003 May 25 '12 at 19:22

2 Answers2

6

No, because while C inherits from B, A<C> does not inherit from A<B>.

To understand why this is the case, imagine if A<T> were instead List<T>:

class B { }

class C : B { }

class D : B { }

class My1  {
    public My1(List<B> lessDerivedTemplateParameter)
    {
       // This is totally legal
       lessDerivedTemplateParameter.Add(new D());
    }
}

class My2 : My1 {
    public My2(List<C> moreDerivedTemplateParameter)
        // if this were allowed, then My1 could add a D to a list of Bs
        : base(moreDerivedTemplateParameter)
    {
    }
}

Now on the other hand, this is legal:

interface IA<out T> { 
    public T GetSome();
}

class B { }

class C : B { }

class D : B { }

class My1  {
    public My1(IA<B> lessDerivedTemplateParameter)
    {
       // This is totally legal
       var someB = lessDerivedTemplateParameter.GetSome();
    }
}

class My2 : My1 {
    public My2(IA<C> moreDerivedTemplateParameter)
        // This is allowed, because an A<C> only *produces* C's (which are also B's)
        // so the base class (which consumes B's, and doesnt care if they are C's) 
        // can use an IA<C>
        : base(moreDerivedTemplateParameter)
    {
    }
}
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • This is correct, but misses the point of the question. He *knows* that the current setup doesn't work, and wants to know if he can take advantage of variance to change that situation. You've just restated what he already knows. – dlev May 25 '12 at 19:22
2

You can declare A as an interface with a contravariant type parameter and it will compile:

internal interface A<out T>
    {
    }

    internal class B
    {
    }

    internal class C : B
    {
    }

    internal class My1
    {
    public My1(A<B> lessDerivedTemplateParameter)
    {
    }
}

internal class My2 : My1
{
    public My2(A<C> moreDerivedTemplateParameter)
        : base(moreDerivedTemplateParameter) 
    {
    }

}
w.brian
  • 16,296
  • 14
  • 69
  • 118