0

I tried a generic class conversion in a more complicated context first only to find out its not possible. So just for demonstration purpose I wrote this sample which is about the most general formulation of my problem I can think of, leaving out anything unnecessary:

using System;
using System.Collections.Generic;

namespace testapp
{
    class Program
    {
        static void Main(string[] args)
        {
            // problem
            Generic<Sub> subvar = new Generic<Sub>();
            Generic<Super> supervar;
            supervar = (Generic<Super>)subvar; // <--fails
            // reason for asking
            List<Generic<Super>> list = new List<Generic<Super>>();
            list.Add(supervar);
        }
    }

    class Super { }

    class Sub:Super { }

    class Generic<T> where T : Super { }
}

So in general: Since every "Sub" is a "Super" I thought that every "Generic of Sub" is a "Generic of Super". So I expected that assigning a reference to a Generic<Sub> to a variable of Generic<Super> would be no problem, even without the explicit typecast.

But .NET Framework tells me this conversion is forbidden, why?

So, why would I want to do this? Suppose I would like to build a Collection that only accepts "Generics of anything", how would I do this circumventing the conversion?

wulf 21
  • 15
  • 3

2 Answers2

3

You have stumbled into the wonderful world of generic variance. Generic classes are always type invariant; in other words C<T1> cannot be cast to C<T2> unless T1 and T2 are exactly the same type.

Interfaces can be declared contravariant and covariant with in and out respectively. This allows casting "upwards" and "downwards" respectively, but classes cannot.

See https://stackoverflow.com/a/2720046/7122 for a very good explanation of variance and why List is invariant.

Community
  • 1
  • 1
David Arno
  • 42,717
  • 16
  • 86
  • 131
  • Thanks for the quick hint to covariant and contravariant interfaces. I will add them into my project. If I can achieve what I am trying to do with them I will mark this as the answer. – wulf 21 Jul 27 '15 at 09:32
3

As written here: https://msdn.microsoft.com/en-us/library/dd469487.aspx you can use the out Generic Modifier to achiev your goals.

For this you would have to add a new Interface that looks like this:

interface IGeneric<out T> where T: Super{}

And then change your generic Class to:

class Generic<T> : IGeneric<T> where T : Super

And you Main would then be:

static void Main(string[] args)
{
    IGeneric<Sub> subvar = new Generic<Sub>();
    IGeneric<Super> supervar;
    supervar = subvar; // No cast required
    List<IGeneric<Super>> list = new List<IGeneric<Super>>();
    ist.Add(supervar);
}
Boot750
  • 891
  • 1
  • 7
  • 17
  • Thank you for your answer. Now my only problem left is that I am undecided, which answer deserves the green check :). I will give it to this one since it is probably the most helpful one for a user who randomly stumbles upon this question. But I strongly recommend reading the other answer, too, in order to understand why the interface is necessary at all. – wulf 21 Jul 27 '15 at 10:44
  • @wulf21, if it helps, I agree that this is a better choice for the "accepted" answer and have upvoted it :) – David Arno Jul 27 '15 at 11:04
  • @wulf21 i'm glad i could help you :) – Boot750 Jul 27 '15 at 11:14