In a 32-bit VCL Application in Windows 10 in Delphi 11 Alexandria, I am trying to write a small app that finds the list of ANCESTORS from a class name that the user inputs in an Edit box:
procedure TForm1.DoShowAncestors(const aClassName: string);
var
ClassRef: TClass;
begin
lstAncestors.Clear;
// Does not work:
//ClassRef := TClass.Create;
//ClassRef.ClassName := aClassName;
// [dcc32 Error] E2076 This form of method call only allowed for class methods or constructor:
ClassRef := TClass(aClassName).ClassType;
while ClassRef <> nil do
begin
lstAncestors.Items.Add(ClassRef.ClassName);
ClassRef := ClassRef.ClassParent;
end;
end;
procedure TForm1.edtClassNameKeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
if Key = VK_RETURN then
begin
DoShowAncestors(Trim(edtClassName.Text));
end;
end;
However, the problem is to transform the input string into a TClass
object. See the above error comments.
CodePudding user response:
Since Delphi is a compiled language, obtaining a class (or object) by name is not a natural operation, but requires some kind of framework.
Fortunately, modern RTTI (uses RTTI
) can easily handle this for you:
procedure ShowAncestors(const AClass: string);
begin
var Ctx := TRttiContext.Create;
try
var LType := Ctx.FindType(AClass);
if LType is TRttiInstanceType then
begin
var R := TRttiInstanceType(LType).MetaclassType;
while Assigned(R) do
begin
ShowMessage(R.ClassName);
R := R.ClassParent;
end;
end;
finally
Ctx.Free; // actually, just to make the code "look" right!
end;
end;
Try it with
ShowAncestors('Vcl.Forms.TForm')
for instance.
(Of course, this only works for classes actually included in the final EXE.)
CodePudding user response:
Now there is no more need to enter a fully qualified class name:
function FindMyClass(const Name: string): TClass;
var
ctx: TRttiContext;
ThisType: TRttiType;
ThisList: TArray<TRttiType>;
begin
Result := nil;
ctx := TRttiContext.Create;
try
ThisList := ctx.GetTypes;
for ThisType in ThisList do
begin
if ThisType.IsInstance and (EndsText(Name, ThisType.Name)) then
begin
Result := ThisType.AsInstance.MetaClassType;
BREAK;
end;
end;
finally
ctx.Free;
end;
end;
procedure TForm1.DoShowAncestors(const aClassName: string);
var
ClassRef: TClass;
begin
lstAncestors.Items.BeginUpdate;
try
lstAncestors.Clear;
ClassRef := FindMyClass(aClassName);
while ClassRef <> nil do
begin
lstAncestors.Items.Add(ClassRef.ClassName);
ClassRef := ClassRef.ClassParent;
end;
finally
lstAncestors.Items.EndUpdate;
end;
end;