从类数组中创建实例

3

我的类定义如下:

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;

那么,假设您有一个表单,上面有三个按钮。一个“狗”按钮,一个“猫”按钮和一个“处理队列”按钮。当您按下“狗”或“猫”按钮时,相关类被添加到数组中充当队列。然后,当您按下“处理队列”按钮时,程序遍历数组,创建相应类的对象,然后调用该类中实现的接口方法。请记住上面的示例代码,如何才能实现这一点?
显然,简单的方法是将类名作为字符串添加到字符串数组中,然后在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;

我尽力避免这种情况,因为每次我添加新的类时,都需要记得为新类添加一个if块。

@J...添加了一个明确的问题陈述,而不是暗示一个。在这种情况下,我正在使用Delphi Seattle。 - Edrean Ernst
1个回答

11

您可以使用类引用来完成此操作。像这样定义您的类引用类型:

Tips:

类引用是一个指向类的指针,通过它可以动态地创建类或调用其方法。

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 实例。我假设你已经知道如何实现。

使用类引用时需要注意一个微妙之处,即通常在基类中提供虚拟构造函数。这是因为当你使用类引用创建一个实例时,调用的是基类声明的构造函数。如果该构造函数不是虚拟的,则派生类构造函数代码将不会被执行。

当然,以这种方式使用类引用会使接口有些无意义。


类引用是 Delphi/Object Pascal 语言中真正的瑰宝。我非常想念这个特性在 Java 或 C# 中。 - Arnaud Bouchez
1
同时请注意,使用虚构造函数Create是有意义的,否则实现可能会在早期或晚期受到限制。 - Arnaud Bouchez
@DavidHeffernan 谢谢!我的实际实现比这个要复杂一些,但是你的答案对我正确地铺设基础非常有帮助。感谢你详细的回答。 - Edrean Ernst

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接