Delphi:我该使用overload、reintroduce + overload还是不用?

3

在基类中有一个虚拟方法,在子类中被覆盖。

但是,我需要在子类方法中添加一个新的参数,由于参数不同,无法使用“override”声明。

例如:

type
  TFruit = class(TObject)
  public
    constructor Create; virtual;
  end;

  TApple = class(TFruit)
  public
    constructor Create(Color: TColor); override; // Error: Declaration of 'Create' differs from previous declaration
  end;

我知道在这种情况下一个好的做法是创建一个新的方法并使用另一个名称,但很多代码会变得冗余。这个新参数只会影响几行代码...

然后我想到了使用“重载”,但这样就不能与“覆盖”一起使用。所以我问:只使用“重载”有什么问题吗?(Delphi显示警告:方法'Create'隐藏了基类型'TFruit'的虚方法)

我还查看了reintroduce + overload的用法(隐藏上面的警告),但我也看到了关于这种做法的不良建议。你认为呢?

最后,如果我只是不使用它们中的任何一个,只是在子类方法中删除“override”并添加新参数呢?(这会给我同样的警告)

有人对我在这种情况下应该怎么做有什么建议,以保持良好的实践吗?

type
  TFruit = class(TObject)
  public
    constructor Create; virtual;
  end;

  TApple = class(TFruit)
  public
    constructor Create; override;

    // What should I do:
    //  constructor Create(Color: TColor); overload; // Shows a warning
    //  constructor Create(Color: TColor); reintroduce; overload; // Hides the warning
    //  constructor Create(Color: TColor); // Shows a warning
    //  Other solution?
  end;

谢谢!


1
你有一个更大的问题。使用虚构造函数意味着你希望从元类实例化。这意味着你必须只有一个构造函数,即虚构造函数,在派生类中可以被覆盖。 - David Heffernan
1
重新编辑:不管有没有构造函数,现有的答案已经回答了。如果您无法覆盖,那么对指向TSubClass实例的TBaseClass变量调用CodToDesc时将不会运行TSubClass.CodToDesc。就是这样。 - Sertac Akyuz
@Roberto 关于你的编辑:由于已经给出并且高度赞同的答案涉及构造函数,正如你最初的问题所问,我建议回滚编辑并提出一个新问题,在其中可能引用这个问题。 - NGLN
1
不需要提出新问题,这里的答案已经涵盖了。对于普通方法和构造函数,原则大致相同。将方法设为虚方法,可以通过基类引用调用并进行多态分发。您无法更改签名并仍然具有多态分发。您需要更好地了解多态性。这是您的下一个步骤。 - David Heffernan
https://stackoverflow.com/questions/34613100/delphi-reintroduce-and-overload-a-virtual-procedure - Gabriel
1个回答

14

在Delphi中,构造函数不一定要命名为Create()。它们可以被命名为任何你想要的名称。因此,如果你需要引入一个新的参数,并且它只影响了几行代码,我建议创建一个全新的构造函数:

构造函数在Delphi中不必命名为Create(),可以随意命名。因此,如果您需要引入一个新参数,仅对少数代码行产生影响,建议创建一个全新的构造函数:

type
  TFruit = class(TObject)
  public
    constructor Create; virtual;
  end;

  TApple = class(TFruit)
  public
    constructor CreateWithColor(Color: TColor);
  end;

constructor TApple.CreateWithColor(Color: TColor);
begin
  inherited Create;
  // use Color as needed...
end;

大部分的代码仍然可以调用 TApple.Create(),只有少数受影响的代码需要改为调用 TApple.CreateWithColor()

否则,如果必须保留 Create() 名称,请使用 reintroduce,并给它一个默认参数,以便现有代码仍然可以编译:

type
  TFruit = class(TObject)
  public
    constructor Create; virtual;
  end;

  TApple = class(TFruit)
  public
    constructor Create(Color: TColor = clNone); reintroduce;
  end;

constructor TApple.Create(Color: TColor);
begin
  inherited Create;
  // use Color as needed...
end;

请注意,无论哪种方式,如果您正在使用TFruit元类的class创建派生对象实例(这通常是使用virtual构造函数的原因),您将无法调用自定义的TApple构造函数:

type
  TFruit = class(TObject)
  public
    constructor Create; virtual;
  end;
  TFruitClass = class of TFruit;

  TApple = class(TFruit)
  public
    constructor Create(Color: TColor = clNone); reintroduce;
    // constructor CreateWithColor(Color: TColor);
  end;

var
  Fruit: TFruit;
  Cls: TFruitClass;
begin
  Cls := TApple;
  Fruit := Cls.Create; // calls TFruit.Create() since it is not overridden in TApple...
  //...
end;

虚拟构造函数通常被视为类可以通过元类实例化的指示。这里提供的选项不兼容。 - David Heffernan
@DavidHeffernan:我知道了。我已经把它加入到我的答案中了。 - Remy Lebeau
谢谢。我知道你当然知道。;-) - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接