4

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

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 调用)。我的猜测是扩展方法与类型参数问题有关,因为它需要找出类型。

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

4

3 回答 3

2

将您的代码重写到我可以让它在您所说的地方失败的程度 - 问题正如 Thom Smith 所说:PickupTruck继承Car,因此是 a Vehicle<Car, ParkingLot>,而不是 a Vehicle<PickupTruck, ParkingLot>。此外,由于它是通用继承,因此它不可能是其他任何东西。

我知道您的代码只是您遇到的问题的简化表示 - 但如果它足够接近,那么我们可以在这里对整体架构进行一些有用的观察。

我不反对通用基础了解它们的派生对应物——确实它对工厂特别有用;然而,它几乎总是立即排除任何进一步的继承。

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

对我来说,这没有任何意义,因为假设我们ParkingLot今天有一个,但明天我们也会得到一个Hangar(用于存放在掩护下的汽车)——这将需要一个全新的车辆类型,这些车辆类型是不平等的事实上,我们也将派生类型传递给基类Vehicle<TDerived, ...>- ergoParkingLotCar并且HangarCar永远不可能等价,即使两个实例都代表相同的品牌/型号等。

因此,预料到这一点,您已经在拥有 common 的地方进行了继承Car,但是当然,在那一点上,任何继承都是毫无意义的,因为Car是 aVehicle<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);
    }
}

现在您有了车辆类型之间的运行时关系;允许不同的具体类型共享特征,例如ICarIPickup接口上的特征;但是你已经破坏了车辆和它的存储之间的关系,所以如果你需要的话,你可以IVehicle从码头上得到一个FootballPitch或把它开到码头上。RiverBed

于 2012-08-30T14:45:55.043 回答
1

您设置它的方式, aPickupTruck不是 a Vehicle<PickupTruck, CarParkingLot>,而仅仅是 a Vehicle<Car, CarParkingLot>。使用这种无限模板递归可能会非常令人困惑。您可以通过声明PickupTruck : Vehicle<PickupTruck, CarParkingLot>、处理协方差或重构类层次结构以避免混淆模式来解决此特定问题。

于 2012-08-30T14:12:57.043 回答
0

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

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

于 2012-08-30T14:10:29.693 回答