I have two classes: one base class an one derived class
The base class defines a virtual method with a parameter:
function ToName(MsgIfNil:string=''); virtual;
The derived class redefines the method:
function ToName(MsgIfNil:string=''); reintroduce;
The implementation of both methods is similar to this code:
function TBaseClass.ToName(MsgIfNil:string)
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := Self.SomeProperty;
end;
The issue is that:
1) If I do not reintroduce the method in the derived class, but use the regular override keyword, any call to this method triggers an access violation
2) When I call the method from an object being nil, and the presumed class of the objet is TBaseObject, it crashes (AV) instead of calling the base virtual method
If no parameter is defined in the method, the right method is called, without any AV. It works well even if the method in the derived class is overriden.
Note that the above solution works well with objects of any class derived from TBaseClass
How can I define a virtual method that can be called with Self=nil, can be virtual and use parameters?
I certainly must enhance my understanding of internal virtual method call plumbering…
Note: Calling on a nil object is legitimate in my use cases. It is not used to hide exceptions, but to report on non linked objects.
Example: myEdit.Text := APerson.Manager.ToName(‘No manager defined’);
Thanks for any advise on a proper solution
Using Delphi 2010 with upd5
Edit: Adding a more complete example of code that triggers an AV
TBaseClass = class(TObject)
private
FMyName: string;
public
property MyName: string read FMyName;
function ToName(MsgIfNil:string=''):string; virtual;
end;
TDerivedClass = class(TBaseClass)
private
FSpecialName: string;
public
property SpecialName:string read FSpecialName;
function ToName(MsgIfNil:string=''):string; reintroduce;
end;
TBaseClass.ToName(MsgIfNil:string):string;
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := MyName;
end;
TDerivedClass.ToName(MsgIfNil:string):string;
begin
if (Self=nil) then
Result := MsgIfNil
else
Result := SpecialName;
end;
// Now a sample program
var
aPerson: TBaseClass;
aSpecialist: TDerivedClass;
begin
aPerson := TBaseClass.Create;
aPerson.MyName := 'a person';
aSpecialist := TDerivedClass.Create;
aSpecialist.SpecialName := 'a specialist';
aSpecialist := nil; // For example sake, never do this in my use case :)
// This works here,
// but triggers an AV if ToName is marked as override instead of reintroduce
ShowMessage('Name of the specialist: '+aSpecialist.ToName('No specialist!'));
aPerson := nil;
// This triggers an AV, TBaseClass.ToName is never called
ShowMessage('Name of the person: '+aPerson.ToName('No person!'));
end;
The above code may not compile, this is only intended to be a more complete example
Takeway
I now understand that VMT is linked to the object reference and, regardless of the object class, calling a virtual method on a nil object is not possible (the object will not even look at its declared type to get the matching address of the ToName method)
I accepted hvd’s solution because it is really effective for methods that must check vs nil (only one base method to add).
Thanks for all answers,
Calling a virtual method on
nildoesn’t make sense:virtualmeans “check the class type to see which method to call”. There is no class type, so there is no method to call.What you can do is create a nonvirtual method that calls a virtual method:
This ensures that
ToNameImpl, the virtual method, is only called whenSelfis notnil.Edit: By the way, this is exactly what the nonvirtual
TObject.Freedoes to call the virtualTObject.Destroy.