Passing an unknown object to a generic interface (originally an explicit type of a generic collection type)
Basically I got these 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 a common interface ICollection
here because the classes that implement IHasParts need different list types Parts
based on some hardcoded conditions.
Now I am getting an unknown ie object Car
and I need to pass it to the tallest parent that still has a property Parts
:
Car c = new Car() {
Parts = new List<CarPart>() {
// ...
}
};
object o = (object)c;
int partsCount = ((IHasParts<ICollection>)o).Parts.Count; // InvalidCastException
How can i do this? DotNetFiddle
source to share
This is a deviation problem.
You are assuming that because it List<T>
is a subtype ICollection
, then IHasParts<List<T>>
it must also be a subtype IHasParts<ICollection>
. This is not true.
If you want to IHasParts<A>
be a subtype IHasParts<B>
where A
is a subtype B
then you need to do covariance IHasParts
in your type parameter T
(using out
).
public interface IHasParts<out TCollectionType> : where TCollectionType : ICollection
{
TCollectionType Parts { get; }
}
For a type that must be covariant, T
can only be used in covariant positions: method return types, get-only property types, and get-only indexes.
It can no longer be used in contravariant positions: method arguments, property setters / indexer.
source to share
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
source to share