【问题标题】:Implement generic abstract method with properties not present in abstract class使用抽象类中不存在的属性实现通用抽象方法
【发布时间】:2022-01-05 09:36:49
【问题描述】:

我有以下设置:

具有以下简化结构的抽象类Vehicle:

public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
    public string GetBasicInfo(Vehicle v)
    {
        return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
    }
    abstract public string GetVehicleData<T>(T vehicle) where T : Vehicle;
}

然后我有一个类 Car 继承自 Vehicle 并具有自己的一些属性:

public class Car : Vehicle
{
    public bool Is4x4 {get;set;}
    public override string GetVehicleData<Car>(Car vehicle)
    {
        return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", vehicle.Brand, vehicle.Model, vehicle.Is4x4);
    }
}

当我尝试访问 vehicle.Is4x4 时出现错误,即 Car 不包含 Is4x4 的定义。我研究了SO,发现由于抽象父类中没有定义属性Is4x4,因此无法在覆盖方法中调用它。

为了规避这个问题,我将 2 个类更改如下:

public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
    public string GetBasicInfo(Vehicle v)
    {
        return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
    }
    abstract public string GetVehicleData(Vehicle vehicle);
}

还有汽车类:

public class Car : Vehicle
{
    public bool Is4x4 {get;set;}
    public override string GetVehicleData(Vehicle vehicle)
    {
        Car vehicleCast = (Car)vehicle;
        return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", vehicleCast.Brand, vehicleCast.Model, vehicleCast.Is4x4);
    }
}

此代码编译并允许我在 GetVehicleData 方法中添加子类特定属性。不过我觉得应该有更好的方法来解决这个常见问题。

【问题讨论】:

  • 为什么GetVehicleData首先需要一个参数?它应该做什么?不应该获取this车辆的数据吗?假设您拨打vehicleA.GetVehicleData(vehicleB)。您希望获得哪辆车的数据?

标签: c# generics abstract


【解决方案1】:

问题出在这一行:

public override string GetVehicleData<Car>(Car vehicle)

这和写法一样:

public override string GetVehicleData<T>(T vehicle)

...除了您使用Car 作为类型参数的名称,而不是更传统的TCar 没有引用类 Car,而是引用了一个新的泛型类型参数。

当您覆盖泛型方法时,语言不会强制您使用相同的类型参数名称:您可以编写

public abstract class Vehicle
{
    public abstract string GetVehicleData<T>(T vehicle) where T : Vehicle;
}

public class Car : Vehicle
{
    public abstract string GetVehicleData<T2>(T2 vehicle) { ... }
}

...您已选择使用Car 而不是T2


要解决根本问题,您不能定义基本方法Vehicle.GetVehicleData(Vehicle vehicle),然后用更具体的Car.GetVehicleData(Car car) 覆盖它。如果有人做了new Car().GetVehicleData(new Truck())会叫什么?

Car 的GetVehicleData 方法需要能够接受任何类型的Vehicle

也就是说,尚不清楚为什么您的 GetVehicleData 会占用第二辆车,而不是仅在其定义的实例上运行。

这样写会更正常:

public abstract class Vehicle
{
    public string Brand { get; set; }
    public string Model { get; set; }
    public string GetBasicInfo()
    {
        return string.Format("This car was produced by {0}. The Model is {1}.", Brand, Model);
    }
    public abstract string GetVehicleData();
}

public class Car : Vehicle
{
    public bool Is4x4 {get;set;}
    public override string GetVehicleData()
    {
        return String.Format("Brand: {0}, Model: {1}, Is4x4: {2}", Brand, Model, Is4x4);
    }
}

【讨论】:

  • GetVehicleData 确实可以重新制定为仅使用其自己的属性而不是采用参数。我在这里简化了太多。我的实际方法需要将另一辆车作为输入。正如您所说,理论上可以调用 Class Car 中的方法 GetVehicleData 传递任何类型的车辆,这就是我得到编译器错误的原因。是否可以将参数限制为仅乘坐汽车?例如。我知道我所有的车辆都应该有一个方法“CopyDataFromSameVehicle”。然后,根据车辆的类型,我想复制特定的属性(Is4x4、HasHelmet ecc)。
  • @Stefan 如果你这样做,你不能将它定义为Vehicle 上的抽象方法。想一想:如果有人拨打Vehicle vehicle = new Car(),他们需要能够拨打vehicle.GetVehicleData(...)。他们有一个Vehicle 类型的变量,即使实际运行时类型是Car。如果Vehicle.GetVehicleData 的签名说它可以接受任何类型的Vehicle,那么人们可以调用它并传入任何类型的Vehicle,并且运行时类型需要能够处理它
  • 那么正确的方法是为每个采用子类特定类型的子类创建一个 GetVehicleData 方法吗?
  • @Stefan 由于我所描述的原因,您不能使用虚拟方法来做到这一点。你的Car 可以定义一个非虚拟的GetVehicleData(Car car) 方法,并且只有当你有一个Car 类型的变量时你才能调用它。
  • 是的,这就是我的意思。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-06
  • 1970-01-01
  • 2021-12-29
  • 2016-08-16
  • 1970-01-01
  • 2012-09-21
相关资源
最近更新 更多