C# 使用派生属性级联派生类

问题描述

所以我正在设计一个具有多个类的框架,这些类以级联方式从另一个类派生出来,以便执行越来越多的特定任务。每个类也有自己的设置,它们是自己的类。但是,设置应该与类本身具有并行继承关系。对于这个问题的性质来说,重要的是设置必须与类本身分开,因为它们具有特定于我的框架的约束。 (编辑:我还想避免使用设置的组合模式,因为它极大地使我使用的框架中的工作流程复杂化。因此设置必须是单个对象,而不是多个对象的组合。)那是我认为这有点像两个平行的类层次结构。一个相关的例子可能是这样的:

假设您有一个 Vehicle 类和一个伴随类 VehicleSettings,用于存储每辆车应具有的相关设置,例如 topSpeedacceleration。>

然后假设您现在有一个 Car 和一个 Airplane 类,它们都继承自 Vehicle 类。但现在您还创建了 CarSettings,它继承自 VehicleSettings 类,但添加了成员​​ gear。然后还有 AirplaneSettings,继承自 VehicleSettings,但添加成员 drag

为了展示我如何继续这种模式,假设现在我有一个新类 SportsCar,它继承自 Car。我创建了相应的设置 SportsCarSettings,它继承自 CarSettings 并添加了一个成员 sportMode

设置这样的类层次结构以便我也可以为设置考虑派生类的最佳方法是什么?

理想情况下,我希望这样的例子像这样工作:

public class VehicleSettings
{
    public float topSpeed;
    public float acceleration;
    // I am explicitly not adding a constructor as these settings get initialized and
    // modified by another object
}

public class Vehicle
{
    public float speed = 0;
    protected VehicleSettings settings;
    
    // Similarly here,there is no constructor since it is necessary for my purposes 
    // to have another object assign and change the settings
    
    protected virtual float SpeedEquation(float dt)
    {
        return settings.acceleration * dt;
    }
    
    public virtual void UpdateSpeed(float dt)
    {
        speed += SpeedEquation(dt)
        if(speed > settings.topSpeed)
        {
            speed = settings.topSpeed;
        }
    }
}

public class CarSettings : VehicleSettings
{
    public int gear;
}
    
public class Car : Vehicle
{
    // Won't compile
    public CarSettings settings;
    
    // Example of the derived class needing to use
    // its own settings in an override of a parent
    // method
    protected override float SpeedEquation(float dt)
    {
        return base.SpeedEquation(dt) * settings.gear;
    }
}

public class AirplaneSettings : VehicleSettings
{
    public float drag;
}
    
public class Airplane : Vehicle
{
    // Won't compile
    public Airplane settings;
    
    // Another example of the derived class needing to use
    // its own settings in an override of a parent
    // method
    public override void UpdateSpeed(float dt)
    {
        base.UpdateSpeed(dt) 
        speed -= settings.drag * dt;
    }
}

public class SportsCarSettings : CarSettings
{
    public bool sportMode;
}
    
public class SportsCar : Car
{
    // Won't compile
    public SportsCarSettings settings;
    
    // Here is again an example of a further derived class needing
    // to use its own settings to override a parent method
    // This example is here to negate the notion of using generic
    // types only once and not in the more complicated way mentioned
    // below
    public override float SpeedEquation(float dt)
    {
        return (settings.acceleration + (settings.sportMode ? 2 : 1)) * dt;
    }
}

看几个可能的解决方案

  1. 使用泛型类型。例如,我可以拥有它以便它是 public class Vehicle<T> : where T : VehicleSettings 和我可以让它有 T settings 行,这样当 CarAirplaneVehicle 继承时,他们可以这样做:{{1} } 和 public Car<T> : Vehicle<T> where T : CarSettings,依此类推。 如果我想实例化 public Airplane<T> : Vehicle<T> where T : AirplaneSettings 而不插入泛型类型,这确实会使事情变得有些复杂。因为那样我就必须创建一个 Car 的子类 Car,如下所示:Car<T>。而且我必须对每个派生类型都做类似的事情。

  2. 在必要的方法中使用类型转换。例如,我可以按如下方式修改 public class Car : Car<CarSettings> { } 类:

    Car
  3. 我还看到了一个建议使用 this 示例中的属性,但这似乎很笨拙,主要是因为它似乎并没有真正解决问题。即使从属性返回动态类型,您仍然必须将返回的值转换为所需的类型。如果这是正确的方法,我将不胜感激有关如何正确实施的解释。

  4. 可以使用 public class Car : Vehicle { // Don't reassign settings and instead leave them // as VehicleSettings // Cast settings to CarSettings and store that copy // locally for use in the method protected override float SpeedEquation(float dt) { CarSettings settings = (CarSettings)this.settings; return base.SpeedEquation(dt) * settings.gear; } } 关键字隐藏父类的 new 版本并将其替换为相应子类设置的名为 settings 的新变量类型,但我认为通常不建议这样做,主要是为了使与原始父类的“设置”的关系复杂化,这会影响继承方法中该成员的范围。

所以我的问题是这个问题的最佳解决方案是什么?这是其中一种方法,还是我遗漏了 C# 语法中非常重要的一些东西?

编辑:

既然有人提到了 Curiously Recurring Template Pattern 或 CRTP,我想提一下我认为它的不同之处。

在 CRTP 中,您有类似 settings 的内容。或者类似地,您可能会遇到类似 Class<T> : where T : Class<T> 之类的问题。

然而,这更多地是关于一个类需要与一个与其自身类型相同的对象进行交互,或者一个派生类需要与需要与派生类对象交互的基类对象进行交互。 无论哪种方式,那里的关系都是循环的。

在这里,关系是平行的。并不是说汽车会与汽车互动,也不是跑车会与车辆互动。它是汽车需要有汽车设置。 SportsCar 需要有 SportsCar 设置,但是当您向上移动继承树时,这些设置只会发生轻微的变化。所以我认为,如果像 C# 这样的深度面向对象的语言需要跳过这么多圈来支持“并行继承”,或者换句话说,在它们的关系中“进化”的不仅仅是对象本身,这似乎有点荒谬从父母到孩子,还有成员自己这样做。

当您没有强类型语言时,例如 Python,您可以免费获得这个概念,因为对于我们的示例,只要我分配给对象的任何特定实例的设置具有相关属性,它的类型无关紧要。所以我认为有时强类型范式可能会成为一个障碍,因为我希望对象由其可访问的属性定义,而不是在这种情况下通过设置定义的类型。 C# 中的强类型系统迫使我制作模板的模板或其他一些奇怪的结构来解决这个问题。

编辑 2:

我发现选项 1 存在重大问题。假设我想制作一个车辆列表和一个设置列表。假设我有一个 Derived<T> : T where T : Base<Derived>、一个 Vehicle、一个 Car 和一个 Airplane,我想将它们放入一个列表中,然后遍历车辆和设置列表并将每个设置分配给各自的车辆。设置可以放在SportsCar类型的列表中,但是没有任何类型(除了VehicleSettings)可以放入车辆,因为Object的父类是{{ 1}},Car 的父类是 Vehicle 等。因此,泛型丢失的是干净的父子层次结构,这使得将相似的对象分组到列表、字典等中变得如此舒适。

但是,使用选项 2,这是可能的。如果没有办法避免上述问题,选项 2 尽管在某些方面不舒服,但似乎是最容易控制的方法。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)