3
public class Base {...}

public class Complex: Base {...}

public class MyOtherClass<T> {...}

and a list of the two type

List<MyOtherClass<Complex>> listOfComplex
List<MyOtherClass<Base>> listOfBase

I want have a list

listOfComplex.Union(listOfBase)

but i cant convert generic of a type to a generic of another type even if Complex derive fro Base It's possibble to have a list of a base class?

user3401335
  • 2,335
  • 2
  • 19
  • 32

4 Answers4

4

Although Complex derives from Base, there is no such relationship between MyOtherClass<Complex> and MyOtherClass<Base>. That is the reason why you cannot create an Union of the two lists. For the framework both generic types are completely different.

The solution now really depends on what exactly your class does. Check out the topic of covariance and contravariance here in Docs - these are two special cases in which you will be permitted to create conversions between the two generic types if the types either are input or output only.

You could also add custom cast or manually cast the items to the "base" type before doing the "union" operation.

Martin Zikmund
  • 38,440
  • 7
  • 70
  • 91
2

Because that's how Covariance and Contravariance in Generics works, specifically invariance, it means that you can use only the type originally specified; so an invariant generic type parameter is neither covariant nor contravariant.

You can't assign an instance of List<Base> to a variable of type List<Complex> or in the opposite direction. Same applies to custom generic classes. There is no implicit conversion that compiler could use in order to convert types.

Even if you add generic type constraint. A<B> and A<C> are two different types with no conversion between them even if C is inherited from B (because AC is not inherited from AB)

Fabjan
  • 13,506
  • 4
  • 25
  • 52
1

@MartinZikmund has explained why it does not work. The resolution to this kind of problem is to either derive the generic classes from a non-generic base class or to let them implement a common interface.

    public class MyOtherClassBase { }
    public class MyOtherClass<T> : MyOtherClassBase { }

Then you can specify MyOtherClassBase as generic parameter to Union explicitly. Now, Union will expect inputs of type IEnumerable<MyOtherClassBase>. Lists of type List<MyOtherClass<T>> are assignment compatible to IEnumerable<MyOtherClassBase>.

var result = listOfComplex.Union<MyOtherClassBase>(listOfBase);

Note the out keyword in the declaration

public interface IEnumerable<out T> : System.Collections.IEnumerable

It makes the interface covariant. See also SO question <out T> vs <T> in Generics and especially Reed Copsey's answer.

Olivier Jacot-Descombes
  • 104,806
  • 13
  • 138
  • 188
0

You should specify in your generic that T is a class base on Type :

public class MyOtherClass<T> where T : Base {...}

T will be accepted only if derive from Base And Union will should work

Here some documentation about Generics, thanks to @Marie:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters

OrcusZ
  • 3,555
  • 2
  • 31
  • 48
  • 1
    You may want to include a link to the official documentation.I assume the OP is not familiar with [generic type constraint](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters) – Marie Mar 25 '19 at 15:50
  • 1
    Also, to expand on this answer @user3401335, Without the generic constraint (`where T : Base`) you could do `new List>`. Obviously `Complex` and `Apple` are not going to be compatible. To ensure that it doesnt blow up Union will verify the type of the incoming `List`s to make sure they are compatible. – Marie Mar 25 '19 at 15:51
  • 1
    Even with the type constraint it does not work, because the constraint does not create a inheritance relationship between the two. – Olivier Jacot-Descombes Mar 25 '19 at 16:03