不能使用二级派生类作为通用基类类型的参数

时间:2012-08-30 14:00:27

标签: c# generics inheritance

我有一个有趣的情况,某些事情正在发挥,但其他事情没有,我不知道为什么。下面是与我的情况相近的代码。我在存储库中有一个静态,它采用由基类对象实现的泛型类型。然后,我基于该泛型类型有两个级别的派生类。派生类的第一级填充基类型的泛型参数并且工作正常,但是从填充泛型参数的类派生的任何类都不能替代它派生自的基类。

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
    }
}

某些方面似乎在PickupTruck理解其通用基础方面正常工作,但扩展(此处未显示)并将类型传递给类型参数(GetDrivableVehicle调用)。我的猜测是扩展方法与类型参数问题有关,因为它需要弄清楚类型。

任何想法为什么这不起作用和/或可以做些什么来解决它?

3 个答案:

答案 0 :(得分:2)

将代码重写到可以让它失败的地方 - 问题就像Thom Smith说的那样:PickupTruck继承Car因此是{{1 },而不是Vehicle<Car, ParkingLot>。此外,由于它的通用继承,它不可能是除此之外的任何其他东西。

我知道你的代码只是你所遇问题的简要表示 - 但如果它足够接近,那么我们可以在这里就整体架构做一些有用的观察。

我并不反对通用基地了解他们派生的同行 - 事实上它对工厂特别有用;但它几乎总是立即排除任何进一步的继承。

您尝试在类型级别编码太多信息;除了我们在这里看到的尖括号的数量之外,它实际上暗示了Vehicle<PickupTruck, ParkingLot>确定它可以存储在其中的存储类型的略微不协调的性质。

对我来说,这没有任何意义,因为我们今天有一个Vehicle<TVehicleType, TStorage>,但明天我们也得到一个ParkingLot(对于汽车存放在掩护下) - 这将需要由于我们也将派生类型传递到基础Hangar - ergo Vehicle<TDerived, ...>ParkingLotCar可以从不通过车辆类型的全新类型是等效的,即使两个实例都代表相同的品牌/模型等。

所以,在预期的情况下,你已经去了一个共同HangarCar的继承,但当然在那时任何继承都没有意义,因为Car是一个Car所以任何派生的从它也必须。只有多重继承可能不是这种情况,但即便如此,它也无法解决整个Vehicle<Car,...>问题。

问自己为什么 ParkingLot需要知道派生类型?是这样你可以有一个工厂方法吗?在这种情况下,您应该将其放入Vehicle<,>Dealership类型;不是车辆基地:

ParkingLot

现在您拥有车辆类型之间的运行时关系;允许不同的具体类型共享特征,例如public interface IVehicle {} public interface ICar : IVehicle {} //because pickup trucks share some car traits public interface IPickup : ICar, IVehicle {} public interface IStorage {} public class Car : ICar, IVehicle {} public class Pickup : IPickup, ICar, IVehicle {} public class ParkingLot : IStorage {} public class Hangar : IStorage {} public class Dealership { public static TVehicle GetVehicle<TVehicle>(IStorage storage) where TVehicle : IVehicle, new() { } } //now you can specialise if you really need to public class CarDealership { public static Car GetVehicle(IStorage storage) { return Dealership.GetVehicle<Car>(storage); } } public class PickupDealership { public static Pickup GetVehicle(IStorage storage) { return Dealership.GetVehicle<Pickup>(storage); } } ICar接口上的特征;但是你已经打破了车辆和它的存储之间的关系,所以你可以从IPickup获得IVehicle,或者如果你需要,可以从码头开出FootballPitch

答案 1 :(得分:1)

您设置的方式,PickupTruck不是Vehicle<PickupTruck, CarParkingLot>,而只是Vehicle<Car, CarParkingLot>。这种无限的模板递归可能会让人很困惑。您可以通过声明PickupTruck : Vehicle<PickupTruck, CarParkingLot>,通过修改co [ntra]方差,或通过重构类层次结构来避免混淆模式来解决此特定问题。

答案 2 :(得分:0)

您正在尝试使用派生类来代替基类。这不行。您应该能够使用BASE类而不是更多派生类,而不是相反。您可以将Car转换为接口(ICar)并让PickupTruck实现ICar。那会有用。

阅读逆变与协方差。 http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx