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 ie 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>
Add an abstract class to take care of specifying the ICollection type. Declare your code something like this:
public interface IHasParts
{
ICollection Parts { get; }
}
public abstract class HasParts<TCollectionType, TPartType> : IHasParts where TCollectionType : ICollection
{
public TCollectionType Parts;
ICollection IHasParts.Parts { get { return this.Parts; } }
}
public class CarPart
{
//...
}
public class Car : HasParts<List<CarPart>, CarPart>
{
protected void AddParts()
{
this.Parts.Add(new CarPart());
}
}
UPDATE:
Here is an updated version of your DotNetFiddle: https://dotnetfiddle.net/O3JZgc
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.