【问题标题】:Create instances from array of classes从类数组创建实例
【发布时间】:2016-05-09 19:51:06
【问题描述】:

我的班级定义是:

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”按钮时,相关类将添加到数组中以充当队列。然后当您按下“处理队列”按钮时,程序将遍历数组,创建相关类的对象,然后调用在该类中实现的接口方法。记住我上面的示例代码,如何实现?

显然,简单的方法是将类名作为字符串添加到字符串数组中,然后在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。

标签: arrays delphi oop polymorphism delphi-10-seattle


【解决方案1】:

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

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# 中的这个特性!
  • 另外请提一下,使用虚拟构造函数 Create 确实有意义,否则实现迟早会受到限制。
  • @DavidHeffernan 谢谢!我的实际实现比这要复杂一些,但是您的回答对于帮助我正确布局基础非常宝贵。感谢您的详细回答。
猜你喜欢
  • 2019-05-19
  • 1970-01-01
  • 2014-03-03
  • 2014-10-04
  • 2021-02-02
  • 1970-01-01
  • 2013-12-02
  • 2014-12-12
  • 1970-01-01
相关资源
最近更新 更多