【问题标题】:How do I create an instance from a string that provides the class name?如何从提供类名的字符串创建实例?
【发布时间】:2009-12-08 10:50:03
【问题描述】:

最近我发现了一段代码,它可以从一个字符串中创建一个 TButton 的实例:'TButton' 被用作参数。

"Is there a way to instantiate a class by its name in Delphi?"

我正在尝试将任何对象的已发布属性保存到 XML 文件(工作正常),最近我想从 XML 文件重新创建这些对象。在这个文件中写入了应该创建哪个类(例如 TButton),然后是一个属性列表,这些属性应该加载到这个运行时创建的对象中。

上面的例子展示了如何做到这一点,但它不适用于我自己的班级。见以下代码:

  TTripple=class (TPersistent)
    FFont:TFont;
  public
    constructor Create;
    Destructor Destroy;override;
  published
    property Font:TFont read FFont write  FFont;
  end;
var
  Form1: TForm1;


implementation

{$R *.dfm}

constructor TTripple.Create;
  begin
  inherited;
  FFont:=TFont.Create;
  end;


destructor TTripple.Destroy;
  begin
  FFont.Free;
  inherited;
  end;

procedure TForm1.FormCreate(Sender: TObject);
begin
RegisterClasses([TButton, TForm, TTripple]);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  CRef : TPersistentClass;
  APer : TPersistent;
begin
 // CRef := GetClass('TButton');
  CRef := GetClass('TTripple');
  if CRef<>nil then
  begin
     APer := TPersistent(TPersistentClass(CRef).Create);
     ShowMessage(APer.ClassName);  // shows TTripple, what is correct
     if APer is TTripple then (APer as TTripple).Font.Color:=90;

     /// Here  I get error message, because TTriple was not created... ?!?!?!

  end;
end;

我无法通过。 TTripple 对象可能已创建,但未使用其构造函数。

【问题讨论】:

    标签: delphi


    【解决方案1】:

    没有调用 TRipple 构造函数,因为它不是虚拟的。

    当您从类引用构造对象时,编译器还不知道最终的类类型是什么,因此它无法在代码中分配正确的构造函数。它只知道它是 TPersistent 的后代,所以它写出代码来调用 TPersistent 的构造函数,即 TObject.Create。如果你想调用正确的构造函数,你必须虚拟地进行。

    已经为从类名读取类定义了一个虚拟构造函数。它在 TComponent 中定义。让 TRipple 从 TComponent 继承并覆盖它的虚拟构造函数(以 Owner 作为参数的构造函数),然后您的代码就可以工作了。

    【讨论】:

      【解决方案2】:

      您可能不想使用 TComponent,还有另一种方法。

      添加对你的类的引用

      TTrippleClass = class of TTripple;
      

      然后你的按钮点击变成:

      procedure TForm1.Button1Click(Sender: TObject);
      var
        CRef : TTrippleClass;
        APer : TPersistent;
      begin
        CRef := TTrippleClass(GetClass('TTripple'));
        if CRef<>nil then
        begin
          APer := TTripple(TTrippleClass(CRef).Create);
          ShowMessage(APer.ClassName);  // shows TTripple, what is correct
          if APer is TTripple then (APer as TTripple).Font.Color:=90;
        end;
      end;
      

      现在您可能希望拥有多个 Tripple 类型,然后创建一个自定义祖先。

      TCustomTripple = class(TPersistent)
      public
        constructor Create;virtual;
      end;
      
      TCustomTrippleClass = class of TCustomTripple;
      
      TTripple = class(TCustomTripple)
      strict private
        fFont : TFont;
      public
        constructor Create;override;
        destructor Destroy;override;
        property Font : TFont read fFont;
      end;
      
      
      constructor TCustomTripple.Create;
      begin
        inherited Create;
      end;
      
      constructor TTripple.Create;
      begin
        inherited;
        fFont := TFont.Create;
      end;
      
      destructor TTripple.Destroy;
      begin
        fFont.Free;
        inherited;
      end;
      
      procedure TForm1.Button1Click(Sender: TObject);
      var
        CRef : TCustomTrippleClass;
        APer : TCustomTripple;
      begin
        CRef := TCustomTrippleClass(GetClass('TTripple'));
        if CRef<>nil then
        begin
          APer := TCustomTripple(TCustomTrippleClass(CRef).Create);
          ShowMessage(APer.ClassName);  // shows TTripple, what is correct
          if APer is TTripple then (APer as TTripple).Font.Color:=90;
        end;
      end;
      

      【讨论】:

      • 你试过编译这个吗?由于 TCustomTripple 的双重声明(作为 'TPersistent' 和 'class of TCustomTripple'),它不会起作用。
      • 我做了,但随后(手动)复制了它,因为我在另一台机器上安装了 Delphi 到我发布答案的机器上。我会检查的。
      • 感谢 Ken,您的第一个示例运行良好。我很感激。在第二个我无法完成 - 正如你所说的那样。为了我的目的,梅森的回答就足够了......
      • +1 用于证明(至少在第一个示例中)构造函数不需要需要是虚拟的。
      猜你喜欢
      • 1970-01-01
      • 2011-04-14
      • 1970-01-01
      • 2015-11-23
      • 2020-07-30
      • 2014-06-04
      • 2011-04-09
      • 2015-07-22
      相关资源
      最近更新 更多