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.