编译器允许在兄弟类中调用受保护的方法,但调用基类

时间:2014-05-13 10:55:18

标签: delphi virtual-method

根据我的想法,下面的代码应该无法编译,因为方法TSubB.DoSomething受到保护,因此TSubA.DoSomething无法看到。 (他们是兄弟姐妹,而不是父母/孩子。)实际上它是编译的,当你运行它时,它实际上会调用TBase.DoSomething。 (因为我忘了DoSomething受到保护,我被这个烧了。)

现在变得奇怪了。如果我将 uBase.pas 中的代码粘贴到 Project1.dpr 并从项目中删除 uBase.pas ,我的确会收到编译错误在那条线上。

有谁可以解释发生了什么?

(很抱歉粘贴这么多代码。这似乎确实是最小的测试用例。)

Project1.dpr

program Project1;
{$APPTYPE CONSOLE}

uses
  uBase in 'uBase.pas',
  uSubB in 'uSubB.pas';

var
  obj : TBase;
begin
  obj := TSubA.Create;
  Writeln(obj.Something);
  obj.Free;
end.

uBase.pas

unit uBase;
interface
type
  TBase = class (TObject)
  protected
    class function DoSomething : string; virtual;
  public
    function  Something : string;
  end;

  TSubA = class (TBase)
  protected
    class function DoSomething : string; override;
  end;

implementation
uses
  uSubB;

function TBase.Something : string;
begin
  Result := DoSomething;
end;

class function TBase.DoSomething : string;
begin
  Result := 'TBase'; // Override in subclass.
end;

class function TSubA.DoSomething : string;
begin
  Result := 'Same as ' + TSubB.DoSomething; // Expect compiler error here
end;
end.

uSubB.pas

unit uSubB;

interface

uses
  uBase;

type
  TSubB = class (TBase)
  protected
    class function DoSomething : string; override;
  end;

implementation

class function TSubB.DoSomething : string;
begin
  Result := 'TSubB';
end;

end.

修改

如果将所有代码从uBase.pas移动到Project1.dpr并从项目中删除uBase.pas,则编译器不再接受对TSubB.DoSomething的调用。我不确定为什么这对编译器的可见性有任何不同。

修改了Project1.dpr

program Project1;
{$APPTYPE CONSOLE}

uses
//  uBase in 'uBase.pas',
  uSubB in 'uSubB.pas';

type
  TBase = class (TObject)
  protected
    class function DoSomething : string; virtual;
  public
    function  Something : string;
  end;

  TSubA = class (TBase)
  protected
    class function DoSomething : string; override;
  end;

function TBase.Something : string;
begin
  Result := DoSomething;
end;

class function TBase.DoSomething : string;
begin
  Result := 'TBase'; // Override in subclass.
end;

class function TSubA.DoSomething : string;
begin
  Result := 'Same as ' + TSubB.DoSomething; // Actual compiler error
end;

var
  obj : TBase;
begin
  obj := TSubA.Create;
  Writeln(obj.Something);
  obj.Free;
end.

1 个答案:

答案 0 :(得分:4)

TSubB.DoSomething确实不可见。但编译器在DoSomething的所有祖先类中查找。它找到的第一个是可见的TBase.DoSomething。因此程序编译。

关于你的编辑,这会改变一切。编辑后,您有两个不同的TBase类。 dpr文件中定义的那个,以及uBase单元中定义的那个。那是你想要的。并且在dpr文件中定义基类确实没有意义,因为单元不能使用dpr文件。