假设我们有一个方法的类可能非常有用,但由于受保护的范围不可用:
unit Sealed;
interface
type
TGeneral = class(TObject)
{ this method is useful, but not available }
protected procedure Useful; virtual;
end;
TSpecific1 = class(TGeneral)
{ some descendants override `Useful` method }
protected procedure Useful; override;
end;
TSpecific2 = class(TGeneral)
{ and some dont, but inherit `Useful`ness from the parent }
end;
我知道两种老派的方式来接触这种方法,两者都涉及继承和类型转换。这两种方法应该与基本案例#1和高级多态案例#2相同。
program CallingSite;
uses Sealed;
function GetInstance: TGeneral;
begin
{ !PSEUDO! makes compiler happy about the rest of code }
// depending on use case supposed to return an instance of `TGeneral`
// or any of its descendants - `TSpecific1`, `TSpecific2`
end;
type
{ this makes a current module a "friend" for `TGeneral` }
TFriend = class(TGeneral)
end;
procedure Case1;
var
{ holds an instance of `TGeneral` }
General: TGeneral;
begin
General := GetInstance;
{ protected method is available for "friend" via static cast }
TFriend(General).Useful; // compiles!
end;
type
TIntroducer = class(TGeneral)
{ this "reintroduces" `Useful` method to public scope }
public procedure Useful; override;
// this approach ought to work even with strict protected methods
// !!! but I THINK it is UNSAFE to use on virtual and/or dynamic methods
end;
procedure TIntroducer.Useful;
begin
{ and calls `Useful` via wrapper }
inherited;
end;
procedure Case2;
var
{ polymorphic instance of any `TGeneral`'s descendant }
Specific: TGeneral;
begin
Specific := GetInstance;
{ protected method is callable via public wrapper, static cast again }
TIntroducer(Specific).Useful; // compiles!
end;
我想知道:
另外,请评论关于TIntroducer
不安全的评论。
答案 0 :(得分:5)
你可以这样使用帮助:
unit Unit2;
interface
type
TGeneral = class(TObject)
protected procedure Useful; virtual;
end;
TSpecific2 = class(TGeneral)
end;
TSpecificHelper = class helper for TGeneral
public
procedure ExposedUseful;
end;
implementation
procedure TGeneral.Useful;
begin
WriteLn('general');
end;
procedure TSpecificHelper.ExposedUseful;
begin
Useful;
end;
end.
甚至可以在单独的单元中声明它们,例如:
unit Unit2;
interface
type
TGeneral = class(TObject)
protected procedure Useful; virtual;
end;
implementation
procedure TGeneral.Useful;
begin
WriteLn('general');
end;
end.
并单独
unit Unit3;
interface
uses
Unit2;
type
TSpecific2 = class(TGeneral)
end;
TSpecificHelper = class helper for TGeneral
public
procedure ExposedUseful;
end;
implementation
procedure TSpecificHelper.ExposedUseful;
begin
Useful;
end;
end.
并测试:
program Project1;
{$APPTYPE CONSOLE}
uses
//Unit2, // either or
Unit3;
var
foo : TSpecific2;
begin
foo := TSpecific2.Create;
foo.ExposedUseful;
Readln;
end.
如果您为基类创建帮助程序,则可以以类似的方式公开私有成员。如果在不同的单位,则需要演员。例如:
// in Unit2
TGeneral = class(TObject)
private
procedure AlsoUseful;
protected
procedure Useful; virtual;
end;
//in Unit3
TSpecificHelper = class helper for TGeneral
public
procedure ExposedUseful;
procedure ExposedAlsoUseful;
end;
// ...
implementation
procedure TSpecificHelper.ExposedAlsoUseful;
begin
TGeneral(self).AlsoUseful;
end;
至于多态性,你真的可以自己测试一下。帮助程序将适用于您的实例派生自的任何后代类:
TSpecific1 = class(TGeneral)
protected
procedure Useful; override;
end;
// ...
procedure TSpecific1.Useful;
begin
WriteLn('specific 1');
end;
,其中
TSpecific2 = class(TSpecific1)
end;
使用上面基类的帮助程序调用时会生成输出specific 1
。
注意强>
从Delphi 10.1 Berlin开始,Class helper无法再访问严格受保护的严格私有或私有成员。这个"功能"实际上是Embarcadero现在在柏林修复的编译器错误
仍然可以使用帮助者访问受保护的普通成员。