I have an interesting situation where certain things are working but others are not and I’m not sure why. Below is code that approximates my situation. I’ve got a static in a repository that takes a generic type which is implemented by a base type of object. I then have two levels of derived classes based on that generic type. The first level of derived class fills out the generic parameters of the base type and works fine, however any class that derives from the class that filled out the generic parameters does not work in place of the base class it is derived from.
public class Vehicle<TVehicleType, TStorage>
{
}
public class Car : Vehicle<Car, ParkingLot>
{
}
public class PickupTruck : Car
{
}
public class Dealership <TDrivableVehicle>
{
public static TDrivableVehicle GetVehicle<TVehicleType, TStorage>(TStorage lot)
where TDrivableVehicle : Vehicle<TVehicleType, TStorage>, new()
{
}
}
public class CarDealership : Dealership<Car>
{
public static Car GetDrivableVehicle(aCarParkingLot)
{
return Dealership.GetDrivableVehicle<Car, CarParkingLot>(aCarParkingLot); <-- Works fine
}
}
public class PickupTruckDealership : CarDealership
{
public static PickupTruck GetDrivableVehicle(aCarParkingLot)
{
return Dealership.GetDrivableVehicle<PickupTruck, CarParkingLot>(aCarParkingLot); <-- fails
}
}
Certain aspects seem to work correctly in terms of PickupTruck understanding its generic base, but extensions (not shown here) and passing the type to a type parameter specifically do not(the GetDrivableVehicle call). My guess is that the extension method is related to the type parameter issue since it would need to figure out the type.
Any ideas why this doesn’t work and/or what can be done to work around it?
Having rewritten your code to a point where I can get it to fail where you say it will – the problem is exactly as Thom Smith says:
PickupTruckinheritsCarand therefore is aVehicle<Car, ParkingLot>, not aVehicle<PickupTruck, ParkingLot>. Also, because of it’s generic inheritance it is impossible for it to be anything else other than that.I know your code is only a boiled down representation of the problem you’re having – but if it’s close enough, then there might be some useful observations we can make here about the overall architecture.
I’m not against generic bases being aware of their derived counterparts – indeed it’s particularly useful for factories; however it almost always instantly rules out any further inheritance.
You’re trying to encode far too much information at the type level; and apart from the number of angle brackets we see here it’s actually hinted at by the slightly incongruous nature that
Vehicle<TVehicleType, TStorage>is determining the type of storage that it can be stored within.To me, that just doesn’t make any sense because let’s say we have a
ParkingLottoday, but tomorrow we also get aHangar(for cars being stored under cover) – this will require a whole new swathe of vehicle types which are unequal by virtue of the fact that we also have the derived type being passed to the baseVehicle<TDerived, ...>– ergoParkingLotCarandHangarCarcan never be equivalent, even if two instances both represent the same make/model etc.So, anticipating that, you have gone for inheritance where you have a common
Car, but of course at that point any inheritance is pointless becauseCaris aVehicle<Car,...>so anything derived from it must also be. Only with Multiple inheritance could that not be the case, but even with that it doesn’t get around the wholeParkingLotquestion.Ask yourself why does
Vehicle<,>need to know about the deriving type? Is it so you can have a single factory method? In that case, you should put it into theDealership, or theParkingLottypes; not the Vehicle base:Now you have the runtime relationships between your vehicle types; allowing different concrete types to share traits such as those on the
ICarorIPickupinterfaces; but you’ve broken the relationship between the vehicle and it’s storage so you can get anIVehiclefrom aFootballPitchor drive it off the pier to theRiverBedif you need to.