Delphi:如何隐藏祖先构造函数?

发布于 2024-09-26 08:15:00 字数 3228 浏览 9 评论 0原文

更新:用一个更简单的例子来解决这个问题,但没有得到回答 根据最初接受的答案

给定以下类及其祖先:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

现在 TCellPhone 有 3 个可见的构造函数:

  • Cup: Integer
  • Cup: Integer; Teapot: string
  • Teapot: string = ''

我该如何处理 TCellPhone 以便祖先构造函数 (Teapot: string = '') 不可见,只留下声明的构造函数:

  • Cup: Integer
  • Cup: Integer;茶壶: 串

注意:通常拥有后代构造函数的简单行为会隐藏祖先:

TCellPhone = 类(TComputer)
民众
   构造函数 Create(Cup: Integer);虚拟的;
结尾;
  • 杯数:整数

如果您想要同时保留 祖先构造函数和 后代,你会标记 后代作为重载

TCellPhone = 类(TComputer)
民众
   构造函数 Create(Cup: Integer);超载;虚拟的;
结尾;
  • 杯数:整数
  • 茶壶:string = ''

在这个问题的示例代码中,Delphi 错误地认为我的 overload 关键字:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

  • 想重载我的带有祖先的构造函数,
  • 当我真的想用兄弟重载它时,

如何隐藏祖先构造函数?

注意: 使用当前定义的 Delphi 语言来隐藏祖先、非虚拟构造函数可能是不可能的。 “不可能”是一个有效的答案。


尝试回答(失败)

尝试reintroduce标记后代构造函数(回退到随机添​​加关键字直到它起作用的模式):

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); reintroduce; overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;

但这没有用,所有三个构造函数仍然可见。 :(


原始问题

我有一个对象,该对象源自具有构造函数不想看到的类:

TEniac = class(TObject)
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create

TComputer = class(TEniac) ...
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)

TCellPhone = class(TComputer)
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

TiPhone = class(TCellPhone)
   constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)

注意:这是一个假设的示例。与现实世界一样,在不破坏现有代码的情况下无法更改祖先对象。

现在,当有人使用 TiPhone 时,我什至不希望他们能够看到来自 TEniac 的构造函数:

iphone := TiPhone.Create(powerCord);

更糟糕的是:如果他们调用该构造函数,他们完全想念我的构造函数,以及其间所做的一切。调用错误的构造函数非常容易,所有这些构造函数在 IDE 代码完成中都是可见的,并且会编译:

TiPhone.Create;

并且它们会得到一个完全无效的对象。

我可以更改 TCellPhone 以在这些构造函数中引发异常:

TCellPhone.Create(PowerCord: TPowercord)
begin
   raise Exception.Create('Don''t use.');
end;

但开发人员不会意识到他们调用了错误的构造函数,直到有一天客户发现错误并向我们罚款数百万美元。事实上,我试图找到我调用错误构造函数的所有地方 - 但我不知道如何让 Delphi 告诉我!

Update: gutted the question with a simpler example, that isn't answered
by the originally accepted answer

Given the following class, and its ancestor:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

Right now TCellPhone has 3 constructors visible:

  • Cup: Integer
  • Cup: Integer; Teapot: string
  • Teapot: string = ''

What do i do to TCellPhone so that the ancestor constructor (Teapot: string = '') is not visible, leaving only the declared constructors:

  • Cup: Integer
  • Cup: Integer; Teapot: string

Note: Usually the simple act of having a descendant constructor hides the ancestor:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); virtual;
end;
  • Cup: Integer

And if you wanted to keep both the
ancestor constructor and the
descendant, you would mark the
descendant as an overload:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
end;
  • Cup: Integer
  • Teapot: string = ''

In this question's example code, Delphi is mistaking my overload keywords:

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

to think that:

  • i want to overload my constructors with the ancestor,
  • when really i want to overload it with the sibling

How do i hide the ancestor constructor?

Note: It might be impossible to hide the ancestor, non-virtual, constructor using the Delphi language as it is currently defined. "Not possible" is a valid answer.


Attempted Answer (failed)

i tried marking the descendant constructors with reintroduce (falling back to my mode of randomly adding keywords until it works):

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); reintroduce; overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); reintroduce; overload; virtual;
end;

But that didn't work, all three constructors are still visible. :(


Original Question

i have an object that descends from a class that has constructors don't want to see:

TEniac = class(TObject)
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create

TComputer = class(TEniac) ...
   constructor Create(PowerCord: TPowerCord=nil); //calls inherited Create(nil)

TCellPhone = class(TComputer)
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

TiPhone = class(TCellPhone)
   constructor Create(sim: TSimChip); //calls inherited Create(sim, 0)

Note: This is a hypothetical example. As in the real world, the ancestor objects cannot be changed without breaking existing code.

Now when someone's using TiPhone i don't want them even being able to see the constructor from TEniac:

iphone := TiPhone.Create(powerCord);

Worse still: if they call that constructor, they completely miss my constructor, and everything done in between. It's pretty easy to call the wrong constructor, all of them are visible in the IDE code-completion, and will compile:

TiPhone.Create;

and they get a completely invalid object.

i could change TCellPhone to throw an exception in those constructors:

TCellPhone.Create(PowerCord: TPowercord)
begin
   raise Exception.Create('Don''t use.');
end;

But developers won't realize they're calling the wrong constructor until the customer finds the error one day and fines us bazillions of dollars. In fact, i'm trying to find everywhere i call the wrong constructor - but i can't figure out how to make Delphi tell me!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

瀟灑尐姊 2024-10-03 08:15:00

在 Delphi 中创建派生类时,不可能使祖先中引入的构造函数无法访问,因为您始终可以这样做:

type
  TComputerClass = class of TComputer;

var
  CellPhoneClass: TComputerClass = TCellPhone;
  CellPhone : TCellPhone;
begin
  CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;

您在任何派生类的代码中所做的任何事情都无法阻止任何人调用 TComputer .创建构造函数来创建派生类的实例。

您可以做的最好的事情是:

TComputer = class(TObject)
public
   constructor Create(Teapot: string=''); virtual;
end;

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string=''); overload; override;
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

在这种情况下,上面的代码至少会调用 TCellPhone.Create(Teapot: string='') 而不是 TComputer.Create(Teapot: string=' ')

It's impossible to ever make a constructors introduced in an ancestor inaccessible for the creation of a derived class in Delphi because you can always do this:

type
  TComputerClass = class of TComputer;

var
  CellPhoneClass: TComputerClass = TCellPhone;
  CellPhone : TCellPhone;
begin
  CellPhone := CellPhoneClass.Create('FUBAR') as TCellPhone;
end;

Nothing you could do in the code of any derived class would ever be able to prevent anyone from calling the TComputer.Create constructor for creating an instance of the derived class.

The best you could do is:

TComputer = class(TObject)
public
   constructor Create(Teapot: string=''); virtual;
end;

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string=''); overload; override;
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

In that case the code above would at least be calling TCellPhone.Create(Teapot: string='') instead of TComputer.Create(Teapot: string='')

一页 2024-10-03 08:15:00

如果我没记错的话,那么 reintroduce 应该有助于虚拟方法。

reintroduce指令禁止编译器发出有关隐藏先前声明的虚拟方法的警告。
当您想用新方法隐藏继承的虚拟方法时,请使用重新引入。

要回答您更新的问题 - 我认为不可能在直接派生类中隐藏具有重载的非虚拟构造函数,但我成功地尝试了以下操作:

TComputer = class(TObject)
public
  constructor Create(Teapot: string='');
end;

TIndermediateComputer = class(TComputer)
protected
  // hide the constructor
  constructor Create;
end;

TCellPhone = class(TIndermediateComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

If I remember correctly, then reintroduce should help for virtual methods.

The reintroduce directive suppresses compiler warnings about hiding previously declared virtual methods.
Use reintroduce when you want to hide an inherited virtual method with a new one.

To answer your updated question - I think it's not possbile to hide a non-virtual constructor with overloading in a directly derived class, but I tried the following successfully:

TComputer = class(TObject)
public
  constructor Create(Teapot: string='');
end;

TIndermediateComputer = class(TComputer)
protected
  // hide the constructor
  constructor Create;
end;

TCellPhone = class(TIndermediateComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;
π浅易 2024-10-03 08:15:00

不要只在重写的无效构造函数中引发“不要使用”异常,而是考虑在它们变得无效的类中将它们标记为已弃用。当错误地使用这些无效的构造函数时,这应该会产生很好的编译器警告。

TCellPhone = class(TComputer)
   constructor Create(PowerCord: TPowerCord=nil); deprecated;
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

此外,根据需要使用覆盖或重新引入。

Instead of only raising an "Don't use" exception in the overridden invalid constructors, consider marking them deprecated in the class where they become invalid. That should produce nice compiler warnings when these invalid constructors are used erroneosly.

TCellPhone = class(TComputer)
   constructor Create(PowerCord: TPowerCord=nil); deprecated;
   constructor Create(sim: TSimChip; UnlockCode: Integer); //calls inherited Create(nil)

In addition, use override or reintroduce as needed.

七堇年 2024-10-03 08:15:00

除非将父类的构造函数声明为虚拟的或动态的,否则您无法隐藏它。但是,您可以阻止从子类中调用它。考虑您的示例:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

TComputer.Create 将始终在 TCellPhone 中可见。您可以通过声明具有相同签名的 TCellPhone.Create 来防止意外调用 TComputer.Create

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string='');
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

然后,只要您在 TCellPhone.Create(Teapot: string='') 正文中没有调用 inherited,就可以阻止 TComputer.通过在 TCellPhone 及其后代中调用来创建。以下内容:

TCellphone.Create;
TCellphone.Create('MyPhone');

将解决 TCellPhone 的实现。

另外:

TiPhone = class(TCellPhone)
    constructor Create;
end;

constructor TiPhone.Create;
begin
  inherited;
end;

将调用 TCellPhone.Create 而不是 TComputer.Create

You can't hide the parent class' constructor unless it was declared virtual or dynamic. You can however prevent it from being called from the child class. Consider your example:

TComputer = class(TObject)
public
   constructor Create(Teapot: string='');
end;

TCellPhone = class(TComputer)
public
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

TComputer.Create will always be visible from TCellPhone. You can prevent TComputer.Create from being inadvertantly called by declaring a TCellPhone.Create with the same signature.

TCellPhone = class(TComputer)
public
   constructor Create(Teapot: string='');
   constructor Create(Cup: Integer); overload; virtual;
   constructor Create(Cup: Integer; Teapot: string); overload; virtual;
end;

Then as long as you don't have a call to inherited in the body of TCellPhone.Create(Teapot: string='') you can prevent TComputer.Create from being called in TCellPhone and its descendants. The following:

TCellphone.Create;
TCellphone.Create('MyPhone');

Will resolve to TCellPhone's implementation.

Additionally:

TiPhone = class(TCellPhone)
    constructor Create;
end;

constructor TiPhone.Create;
begin
  inherited;
end;

Will invoke TCellPhone.Create and not TComputer.Create.

流年已逝 2024-10-03 08:15:00

您想要重新引入构造函数:

TiPhone = class(TCellPhone)
    constructor Create(sim: TSimChip); reintroduce;

请参阅 Delphi 源代码中的 TComponent.Create 以获取实际示例。

You want to reintroduce the constructor:

TiPhone = class(TCellPhone)
    constructor Create(sim: TSimChip); reintroduce;

See TComponent.Create in the Delphi source code for a real-world example of this.

沫尐诺 2024-10-03 08:15:00

我知道这是 5 年前的话题,但它仍然可能对某人有所帮助。
隐藏祖先构造函数的唯一方法是将两个 Create 方法之一重命名为其他方法,并消除对overload 指令的需要。
看起来很奇怪,但这是唯一的方法。至少在旧版本的 Delphi 中是这样。我不知道 XE xxx 版本现在是否可以。

I know that this is 5 years old topic, but still it may help someone.
The only way to hide the ancestor's constructor is to rename one of the two Create methods to something else and remove the need of overload directive.
It looks weird but it's the only way. At least in older versions of Delphi. I don't know is it possible now in the XE xxx versions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文