代码之家  ›  专栏  ›  技术社区  ›  mjn anonym

如何从类引用创建Delphi对象并确保构造函数的执行?

  •  20
  • mjn anonym  · 技术社区  · 16 年前

    如何使用类引用创建对象的实例,以及 确保构造函数被执行?

    在此代码示例中,不会调用tmyclass的构造函数:

    type
       TMyClass = class(TObject)
         MyStrings: TStrings;
         constructor Create; virtual;
       end;
    
    constructor TMyClass.Create;
    begin
       MyStrings := TStringList.Create;
    end;
    
    procedure Test;
    var
       Clazz: TClass;
       Instance: TObject;
    begin
       Clazz := TMyClass;
       Instance := Clazz.Create;
    end;
    
    5 回复  |  直到 10 年前
        1
  •  25
  •   Alex    16 年前

    使用此:

    type
      TMyClass = class(TObject)
        MyStrings: TStrings;
        constructor Create; virtual;
      end;
      TMyClassClass = class of TMyClass; // <- add this definition
    
    constructor TMyClass.Create;
    begin
       MyStrings := TStringList.Create;
    end;
    
    procedure Test;
    var
      Clazz: TMyClassClass; // <- change TClass to TMyClassClass
      Instance: TObject;
    begin
       Clazz := TMyClass; // <- you can use TMyClass or any of its child classes. 
       Instance := Clazz.Create; // <- virtual constructor will be used
    end;
    

    或者,可以使用类型强制转换为tmyclass(而不是“tmyclass类”)。

        2
  •  22
  •   Malte    15 年前

    亚历山大的解决方案很好,但在某些情况下还不够。假设您希望设置一个TclassFactory类,在该类中,可以在运行时存储Tclass引用,并在稍后检索任意数量的实例。

    这样的类工厂永远不会知道它所拥有的类的实际类型,因此无法将它们强制转换为它们的相应的元类。在这种情况下,要调用正确的构造函数,以下方法将起作用。

    首先,我们需要一个简单的演示类(不要介意公共领域,它只是为了演示目的)。

    interface
    
    uses
      RTTI;
    
    type
      THuman = class(TObject)
      public
        Name: string;
        Age: Integer;
    
        constructor Create(); virtual;
      end;
    
    implementation
    
    constructor THuman.Create();
    begin
      Name:= 'John Doe';
      Age:= -1;
    end;
    

    现在我们只通过rtti和正确的构造函数调用来实例化thuman类型的对象。

    procedure CreateInstance();
    var
      someclass: TClass;
      c: TRttiContext;
      t: TRttiType;
      v: TValue;
      human1, human2, human3: THuman;
    begin
      someclass:= THuman;
    
      // Invoke RTTI
      c:= TRttiContext.Create;
      t:= c.GetType(someclass);
    
      // Variant 1a - instantiates a THuman object but calls constructor of TObject
      human1:= t.AsInstance.MetaclassType.Create;
    
      // Variant 1b - same result as 1a
      human2:= THuman(someclass.Create);
    
      // Variant 2 - works fine
      v:= t.GetMethod('Create').Invoke(t.AsInstance.MetaclassType,[]);
      human3:= THuman(v.AsObject);
    
      // free RttiContext record (see text below) and the rest
      c.Free;
    
      human1.Destroy;
      human2.Destroy;
      human3.Destroy;
    end;
    

    您会发现对象“human1”和“human2”已初始化为零,即name=''和age=0,这不是我们想要的。对象human3将保存Thuman的构造函数中提供的默认值。

    但是请注意,此方法要求类具有不带参数的构造函数方法。以上所有这些都不是我构想出来的,而是在 Rob Love's Tech Corner .

        3
  •  11
  •   Stijn Sanders    16 年前

    请检查是否可以在构造后覆盖。

        4
  •  6
  •   Ondrej Kelle    16 年前

    您的代码稍有修改:

    type
      TMyObject = class(TObject)
        MyStrings: TStrings;
        constructor Create; virtual;
      end;
      TMyClass = class of TMyObject;
    
    constructor TMyObject.Create;
    begin
      inherited Create;
      MyStrings := TStringList.Create;
    end;
    
    procedure Test; 
    var
      C: TMyClass;
      Instance: TObject;
    begin
       C := TMyObject;
       Instance := C.Create;
    end;
    
        5
  •  0
  •   alijunior    10 年前

    您可以在基类中创建一个抽象方法,并在构造函数中调用它,并在从类引用创建时在要执行的子类中重写。