Basically I got those sample classes:
public interface IHasParts<TCollectionType> : where TCollectionType : ICollection
{
TCollectionType Parts { get; set; }
}
public class CarPart
{
//...
}
public class Car : IHasParts<List<CarPart>>
{
public List<CarPart> Parts { get; set; }
//...
}
Yes, I need to use an generic interface of ICollection here, because classes that implement IHasParts need different list types of Parts based on some hard programmed conditions.
Now I get an unknown object of i.e. Car and I need to cast it to the highest parent that still has the Parts property available:
Car c = new Car() {
Parts = new List<CarPart>() {
// ...
}
};
object o = (object)c;
int partsCount = ((IHasParts<ICollection>)o).Parts.Count; // InvalidCastException
How can I do that? DotNetFiddle
This is a variance issue.
You're assuming that, because List<T> is a subtype of ICollection, then IHasParts<List<T>> must too be a subtype of IHasParts<ICollection>. It doesn't.
If you want IHasParts<A> to be a subtype of IHasParts<B> where A is a subtype of B, then you need to make IHasParts covariant in its type parameter T (using the out keyword).
public interface IHasParts<out TCollectionType> : where TCollectionType : ICollection
{
TCollectionType Parts { get; }
}
For a type to be covariant, T can only be used in covariant positions: method return types, get-only property types and get-only indexers.
It can no longer be used in contravariant positions: method arguments, property/indexer setters.
If you define your Car class with ICollection instead of List<CarPart>, then works:
public class Car : IHasParts<ICollection>
{
public ICollection Parts { get; set; }
}
You can still initialize your Parts with a List<CarPart>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With