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

+3


source to share


3 answers


If you define your Car class with ICollection

instead List<CarPart>

, then it works:

public class Car : IHasParts<ICollection>
{
    public ICollection Parts { get; set; }
}

      



You can initialize your parts with List<CarPart>

+2


source


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.

+4


source


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

+1


source







All Articles