Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics and casting - cannot cast inherited class to base class

I know this is old, yet I am still not very good with understanding those problems. Can anyone tell me why the following does not work (throws a runtime exception about casting)?

public abstract class EntityBase { } public class MyEntity : EntityBase { }  public abstract class RepositoryBase<T> where T : EntityBase { } public class MyEntityRepository : RepositoryBase<MyEntity> { } 

And now the casting line:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo; 

So, can anyone explain how is this invalid? And, I you are not in the mood to explain - is there a line of code I can use to actually do this cast?

like image 339
Jefim Avatar asked Aug 20 '10 07:08

Jefim


People also ask

Can generic classes be inherited?

Generic classes support an inheritance mechanism and can form hierarchies. Any generic class that takes a type T as a parameter can be inherited by another derived class. In this case, a parameter of type T is passed to the derived class.

Can you cast a generic type?

In the general case, a generic type could be both a supplier and a consumer of its type parameters, which means that the type we get by casting the type parameter (up or down) cannot be either a subtype or a supertype of the original: they are unrelated types that cannot be cast between, which is exactly why the Java ...

Can we inherit generic class in C#?

You cannot inherit a generic type. // class Derived20 : T {}// NO!

Can a generic class be derived from another generic class?

In the same way, you can derive a generic class from another generic class that derived from a generic interface. You may be tempted to derive just any type of class from it. One of the features of generics is that you can create a class that must implement the functionality of a certain abstract class of your choice.


1 Answers

RepositoryBase<EntityBase> is not a base class of MyEntityRepository. You're looking for generic variance which exists in C# to a limited extent, but wouldn't apply here.

Suppose your RepositoryBase<T> class had a method like this:

void Add(T entity) { ... } 

Now consider:

MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;  baseRepo.Add(new OtherEntity(...)); 

Now you've added a different kind of entity to a MyEntityRepository... and that can't be right.

Basically, generic variance is only safe in certain situations. In particular generic covariance (which is what you're describing here) is only safe when you only ever get values "out" of the API; generic contravariance (which works the other way round) is only safe when you only ever put values "into" the API (e.g. a general comparison which can compare any two shapes by area can be considered as a comparison of squares).

In C# 4 this is available for generic interfaces and generic delegates, not classes - and only with reference types. See MSDN for further information, read <plug>read C# in Depth, 2nd edition, chapter 13</plug> or Eric Lippert's blog series on the topic. Also, I gave a one hour talk about this at NDC in July 2010 - the video is available here.

like image 71
Jon Skeet Avatar answered Sep 23 '22 16:09

Jon Skeet