如果后代类'方法的变量与Self相同,是否可以访问其祖先的受保护方法?

Dom*_*mus 5 delphi compiler-construction encapsulation class protected

这个问题来自于使用方法链接时出现的问题(流畅的界面),我认为这是它可能成为问题的唯一原因之一.

为了说明,我将使用方法链接的示例:

在单元A中:

TParent = class
protected
  function DoSomething: TParent;
end;
Run Code Online (Sandbox Code Playgroud)

在单元B中:

TChild = class(TParent)
public
  procedure DoAnotherThing;
end;

implementation

procedure TChild.DoAnotherThing;
begin
  DoSomething.DoSomething
end;
Run Code Online (Sandbox Code Playgroud)

我想保持DoSomething过程受保护,只对类后代可见.

这不会编译,抛出一个

无法访问受保护的符号TParent.DoSomething

因为DoSomething返回一个TParent,后续的DoSomething调用是从另一个单元中的TParent对象发出的(因此保护启动并且函数无法访问).(谢谢David Heffernan的解释)

为了减少它的本质,像TParent(Self).DoSomething在TChild类中是不可能的.

我的问题:

既然编译器确实知道从子类中访问Self参数的副本,那么是否存在访问祖先的受保护方法的能力破坏封装的实例?我只是谈论后代的类方法中取消引用类型化的Self .我知道在这个类之外,该参数当然不应该访问祖先的受保护方法(在另一个单元中).

同样,简而言之:当一个与Self参数相同的变量在其自身的一个类方法中被取消引用时,编译器是否允许它访问其父级的受保护方法是不安全的(就像Self参数本身一样) )?

这是一个非常理论上的问题,但如果编译器允许这样做,我会对编译代码或封装会产生任何负面影响感兴趣.

谢谢.

Ste*_*nke 10

protected表示您可以在自己的实例中访问这些方法.这并不意味着您可以在任何其他类型的实例上访问这些方法.

你可以打电话的原因DoSomethingTChild,因为只有Self访问其祖先的保护成员.

事实上,在这种特殊情况下,DoSomething方法的结果等于 Self编译器无法评估(除了一些静态代码分析,我怀疑其中有任何OOP语言编译器).

C++解决了这个问题,能够使TChild成为TParent的友元类,从而使它能够访问这些方法.

在Delphi中,如果将两个类放在同一个单元中,则可以获得该功能.如果您想将这两个类保留在自己的单元中,您仍然可以通过声明"cracker class"来使用该功能.只需创建一个继承自的类TParent并将其放入同一个单元中TChild.然后,您可以将结果转换为DoSomething该类并访问受保护的方法TParent.

type
  TChild = class(TParent)
  public
    procedure DoAnotherThing;
  end;

implementation

type
  TParentAccess = class(TParent);

procedure TChild.DoAnotherThing;
begin
  TParentAccess(DoSomething).DoSomething;
end;
Run Code Online (Sandbox Code Playgroud)

更新6.12.2017:

您还可以为您的TChild班级添加一个方法来模仿"朋友状态"(感谢Ken Bourassa).

type
  TChild = class(TParent)
  private
    type
      TParentAccess = class(TParent);
    function DoSomething: TParentAccess; inline;
  public
    procedure DoAnotherThing;
  end;

implementation

function TChild.DoSomething: TParentAccess;
begin
  Result := TParentAccess(inherited DoSomething);
end;

procedure TChild.DoAnotherThing;
begin
  DoSomething.DoSomething;
end;
Run Code Online (Sandbox Code Playgroud)

第三种可能性是使用类助手来使方法可访问.这具有易于重复使用的优点,因为您只需要将辅助单元添加到您有孩子TParent且需要访问权限的任何单元.

type
  TParentHelper = class helper for TParent
  public
    function DoSomething: TParent; inline;
  end;

implementation

function TParentHelper.DoSomething: TParent;
begin
  Result := inherited DoSomething;
end;
Run Code Online (Sandbox Code Playgroud)