从 类 数组创建实例

Create instances from array of classes

我的 class 定义是:

TAnimal = class(TInterfacedObject)
public
    constructor Create; overload;
    constructor Create(param : string); overload;
end;

IAnimal = interface
    procedure DoSomething;
end;

TDog = class(TAnimal, IAnimal)
public
    procedure DoSomething;
end;

TCat = class(TAnimal, IAnimal)
public
    procedure DoSomething;
end;

示例代码:

procedure TForm1.DogButtonPressed(Sender: TObject);
var
    myDog : TDog;
    I : Integer;
begin
    myDog := TDog.Create('123');
    I := Length(myQueue);
    SetLength(myQueue, I+1);
    myQueue[I] := TDog; //Probably not the way to do it...??
end;

procedure TForm1.CatButtonPressed(Sender: TObject);
var
    myCat : TCat;
    I : Integer;
begin
    myCat := TCat.Create('123');
    I := Length(myQueue);
    SetLength(myQueue, I+1);
    myQueue[I] := TCat; //Probably not the way to do it...??
end;

procedure TForm1.OnProcessQueueButtonPressed(Sender: TObject);
var
    MyInterface : IAnimal; //Interface variable
    I : Integer;
begin
    for I := Low(myQueue) to High(myQueue) do
    begin
        MyInterface := myQueue[I].Create('123'); //Create instance of relevant class
        MyInterface.DoSomething;
    end;
end;

那么,假设您有一个包含三个按钮的表单。一个 "Dog" 按钮,一个 "Cat" 按钮和一个 "Process Queue" 按钮。当您按下 "Dog" 按钮或 "Cat" 按钮时,相关的 class 将添加到数组中以充当队列。当您随后按下 "Process Queue" 按钮时,程序将逐步遍历数组,创建相关 class 的对象,然后调用在该 class 中实现的接口方法。请牢记我上面的示例代码,如何实现?

简单的方法显然是将 class 名称作为字符串添加到字符串数组,然后在 OnProcessQueueButtonPressed 过程中使用 if 语句,例如:

procedure TForm1.OnProcessQueueButtonPressed(Sender: TObject);
var
    MyInterface : IAnimal; //Interface variable
    I : Integer;
begin
    for I := Low(myQueue) to High(myQueue) do
    begin
        if myQueue[I] = 'TDog' then
            MyInterface := TDog.Create('123');
        if myQueue[I] = 'TCat' then
            MyInterface := TCat.Create('123');            
        MyInterface.DoSomething;
    end;
end;

我试图避免这种情况,因为每次我添加一个新的 class 我都必须记住为新的 class.[=17 添加一个 if 块=]

您可以使用 class reference 来做到这一点。像这样定义你的 class 引用类型:

type
  TAnimalClass = class of TAnimal;

并安排TAnimal支持接口:

type
  IAnimal = interface
    procedure DoSomething;
  end;

  TAnimal = class(TInterfacedObject, IAnimal)
  public
    constructor Create; overload;
    constructor Create(param: string); overload;
    procedure DoSomething; virtual; abstract;
  end;

  TDog = class(TAnimal)
  public
    procedure DoSomething; override;
  end;

  TCat = class(TAnimal)
  public
    procedure DoSomething; override;
  end;

您对数组的使用导致代码相当混乱。更好的方法是使用列表对象。

var
  myQueue: TList<TAnimalClass>; 

现在你可以这样写代码了:

procedure TForm1.DogButtonPressed(Sender: TObject);
begin
  myQueue.Add(TDog);
end;

procedure TForm1.CatButtonPressed(Sender: TObject);
begin
  myQueue.Add(TCat);
end;

procedure TForm1.OnProcessQueueButtonPressed(Sender: TObject);
var
  AnimalClass: TAnimalClass;
  Animal: IAnimal;
begin
  for AnimalClass in myQueue do
  begin
    Animal := AnimalClass.Create('123'); 
    Animal.DoSomething;
  end;
  myQueue.Clear;
end;

您需要在适当的时候创建和销毁 myQueue 的实例。我假设您已经知道该怎么做。

使用 class 引用时的一个细微差别是您通常在基 class 中提供一个虚拟构造函数。那是因为当你使用 class 引用创建实例时,你调用了基 class 中声明的构造函数。如果该构造函数不是虚拟的,那么您派生的 class 构造函数代码将不会被执行。

当然,以这种方式使用 class 引用会使界面变得毫无意义。