Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast unknown object to generic interface of interface (from initially explicit generic type of generic collection type of type)

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

like image 873
Martin Braun Avatar asked Jan 20 '26 16:01

Martin Braun


2 Answers

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.

like image 177
dcastro Avatar answered Jan 23 '26 06:01

dcastro


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>

like image 20
György Kőszeg Avatar answered Jan 23 '26 06:01

György Kőszeg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!