Question

To my thinking, the code below should fail to compile because the method TSubB.DoSomething is protected, thus not visible from TSubA.DoSomething. (They are siblings, not parent/child.) In fact it compiles and when you run it, it actually calls TBase.DoSomething. (I got burned by this because I'd forgotten DoSomething was protected.)

Now it gets weird. If I paste the code from uBase.pas into Project1.dpr and remove uBase.pas from the project, I do indeed get a compiler error on that line.

Can anyone explain what's going on?

(Sorry to paste so much code. This really does seem to be the minimal test case.)

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.

Edit

If you move all the code from uBase.pas into Project1.dpr and remove uBase.pas from the project, then the compiler no longer accepts the call to TSubB.DoSomething. I'm not sure why this is any different in terms of visibility to the compiler.

Revised 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.
Was it helpful?

Solution

TSubB.DoSomething is indeed not visible. But the compiler looks in all ancestor classes for DoSomething. The first one it finds is TBase.DoSomething which is visible. Hence the program compiles.

As to your edit, that changes everything. After your edit you have two different TBase classes. The one defined in the dpr file, and the one defined in the uBase unit. That's you intend. And there's really no point defining base classes in a dpr file because units cannot use dpr files.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top