2

I have the next classes:

public class EntityBase<T>
{
    public T Id { get; set; }

}

And it's implementers:

public class ClassA : EntityBase<Int32>
{
     ...
}

public class ClassB : EntityBase<Int64>
{
     ...
}

And in the code, which dont know about classes - ClassA and ClassB it knows only about existance of the EntityBase<...>, I do something like this:

     // Here for sure I get the list of `ClassA`
     object obj = GetSomeHowListOfClassA();
     List<EntityBase<Int32>> listOfEntityBases = (List<EntityBase<Int32>>)obj;

And I get the error:

Unable to cast object of type 'System.Collections.Generic.List`1[...ClassA]' to type 'System.Collections.Generic.List`1[...EntityBase`1[System.Int32]]'.

I fix it like this:

var listOfEntityBases = new List<EntityBase<Int32>>(obj);

But I dont like this way, because I'm creating new List<>. Is there way to cast it? Thx for any advance.

Maris
  • 4,608
  • 6
  • 39
  • 68
  • try reading these http://stackoverflow.com/questions/2184551/difference-between-covariance-contra-variance http://stackoverflow.com/questions/245607/how-is-generic-covariance-contra-variance-implemented-in-c-sharp-4-0 http://stackoverflow.com/questions/4038125/covariance-in-c-sharp etc – Lanorkin Apr 04 '13 at 10:17
  • Problem is not in understanding of covariance and contravariance. But the problem is that List<> doesnt support contravariance. That's why I'm asking any workaround from this issue.. – Maris Apr 04 '13 at 10:20
  • can you redesign it to use `MyClass : EntityBase` instead of `ClassA` and `ClassB`? – Lanorkin Apr 04 '13 at 10:22
  • I dont think that it will fix the issue. – Maris Apr 04 '13 at 10:24
  • It may allow you to create generics methods - but it really depends on your design and usage. So you don't want new list creation (like this one `List> listOfEntityBases = obj.Cast>().ToList();`) - you are looking for pure cast? – Lanorkin Apr 04 '13 at 10:29
  • yeap. I'm looking for pure cast. ) And also in this part of code I will do some modification and throw it to the other part of my programm in which I will cast it back to the (ClassA). – Maris Apr 04 '13 at 10:37
  • actually I am very confused why you would need to get an object. Have you considered a generic constraint `where T : EntityBase` – Aron Apr 04 '13 at 11:18
  • You would not need to KNOW about `ClassA` as long as you have a generic argument which you place `ClassA` into AND you can still have your `List` – Aron Apr 04 '13 at 11:23

2 Answers2

2

You can not do it for clear reason. Let's assume this line of code will work:

 List<EntityBase<Int32>> listOfEntityBases = (List<EntityBase<Int32>>)obj;

This means that after that line you can do say following

listOfEntityBases.Add(new EntityBase<Int32>());

but actually this line in the same time will add EntityBase<Int32> object to your obj of type List<ClassA> - which is definitely InvalidCast.

So, you just cannot declare the same variable as List<ClassA> and List<EntityBase<Int32>> in the same time.

Though, it is easily allowed for IEnumerable<T> as you can not add new values for such collection.

And that's why they have in and out in generics declaration.

Lanorkin
  • 7,310
  • 2
  • 42
  • 60
  • What Lanorkin is trying to describe is the difference between Covariance and Contravariance. An interface can't be both Covariant and Contravariant without being the same interface. – Aron Apr 04 '13 at 11:00
  • What I'm trying to describe is why it can not be casted. Differences clearely described in links I adviced to @Maris, while they still want to just cast. – Lanorkin Apr 04 '13 at 11:17
0

You can't do cast this way, because:

  • covariance in C# isn't working for classes;
  • interfaces IList<T> and ICollection<T> aren't covariant.

The only option you can do here (except making a copy of a list) is a casting to IEnumerabe<T>:

var listOfEntityBases = (IEnumerable<EntityBase<Int32>>)obj;
Dennis
  • 37,026
  • 10
  • 82
  • 150
  • I personally prefer `IEnumerable> listOfEntityBases = obj`. The compiler will be forced to do an implicit (safe) cast, as opposed to an explicit (potentially unsafe) cast. Heuristically it is safe of course, but if we ever change `ClassA` then it could break at runtime. – Aron Apr 04 '13 at 10:58
  • @Aron: `IEnumerable> listOfEntityBases = obj`- what? This even won't compile. – Dennis Apr 04 '13 at 11:13
  • I mean `var obj = new List(); IEnumerable> listOfEntityBases = obj;` for casting... – Aron Apr 04 '13 at 11:16
  • Ghm... As I wrote in my question - `And in the code, which dont know about classes - ClassA and ClassB it knows only about existance of the EntityBase<...>, I do something like this:` – Maris Apr 04 '13 at 11:17
  • @Aron: the question says, that `obj` has a `System.Object` type. – Dennis Apr 04 '13 at 11:17