简体   繁体   中英

Delphi - how to enum string to type

I got strings in database like 'TGroupBox' or 'TEdit' ... now I need to check element against them... how do I enumerate string to type?

I mean something like this:

mystr := 'TGroupBox';
If (page.Controls[0] is mystr) then ...

Of course it won't work, as error appears:

E2015 Operator not applicable to this operand type

How do I do that correctly?

You can verify that

page.Controls[0].ClassName = mystr

using the ClassName property.

But notice that this doesn't do exactly the same thing as the is operator. To see the difference, suppose you have a class TFruit and a subclass TApple . If myFruit is an instance of a TApple , then both myFruit is TApple and myFruit is TFruit will yield true . But of course, the ClassName will still only be TApple .

If you need the full functionality of the is operator, you can make use of the ClassParent property, as suggested by hvd:

function IsDerivedFrom(AClass: TClass; const AClassName: string): boolean;
begin
  if not Assigned(AClass) then Exit(false);
  result := SameText(AClass.ClassName, AClassName) or
    IsDerivedFrom(AClass.ClassParent, AClassName);
end;

To get the class of an object, use the ClassType property:

IsDerivedFrom(page.Controls[0].ClassType, mystr);

The function you are looking for is GetClass located in System.Classes. Be aware that the class has to be registered.

System.Classes.GetClass

For the specific scenario in the question body the answer by Andreas Rejbrand (with assistance from hvd) is a good one. However, for the broader problem implied by the question title - how to I convert a string containing a class name to a class reference? - you can utilise extended RTTI in a new(ish) version of Delphi:

unit ClassLookupUtils;

interface

uses
  System.SysUtils, System.Generics.Collections, System.Rtti;

type
  RttiClassLookup = record
  strict private
    class var FMap: TDictionary<string, TClass>;
    class destructor Destroy;
  public
    class function Find(const ClassName: string): TClass; static;
  end;

implementation

class destructor RttiClassLookup.Destroy;
begin
  FMap.Free;
end;

class function RttiClassLookup.Find(const ClassName: string): TClass;
var
  RttiType: TRttiType;
  RttiContext: TRttiContext;
begin
  if FMap = nil then
  begin
    FMap := TDictionary<string, TClass>.Create;
    for RttiType in RttiContext.GetTypes do
      if RttiType is TRttiInstanceType then
        FMap.AddOrSetValue(RttiType.Name.ToLowerInvariant, (RttiType as TRttiInstanceType).MetaclassType);
  end;
  if not FMap.TryGetValue(ClassName.ToLowerInvariant, Result) then
    Result := nil;
end;

end.

In use:

var
  MyStr: string;
  MyStrClass: TClass;
begin
  //...
  MyStrClass := RttiClassLookup.Find(MyStr);
  if MyStrClass <> nil then
    for I := 0 to Page.ControlCount - 1 do
      if Page.Controls[I].InheritsFrom(MyStrClass) then
      begin
        //...
      end;

The background here is that SomeObj is SomeClass is implemented as (SomeObj <> nil) and SomeObj.InheritsFrom(SomeClass) .

You have a good answer from @UweRaabe using RTTI to get ClassName .

A simple (and not very robust) hack without using RTTI would be to use the TComponent.Name property, which is a string, like this - without the is operator:

 If (pos('GroupBox', page.Controls[0].name)>0 ) then ...

By default, a control gets the same name as the instance variable, so GroupBox1.name='GroupBox1 '. You can either change your database entries to use the substr 'groupbox' or extract 'groupbox' from the type name string in your database.

That being said, if you've inherited this design approach of persisting type names as strings in a database and then using them at runtime to check the types of different components, then you're stuck with it, and so be it. But Delphi is a strongly typed, compiled language, so persisting type names as strings in a database and reading them at runtime and decoding them into Delphi types just doesn't "smell right" IMO. I would re-think this design if possible. Consider doing it all in Delphi using classOf type, enumerations, etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM