【问题标题】:Generic mechanism for instantiating distinct types in Delphi在 Delphi 中实例化不同类型的通用机制
【发布时间】:2019-02-11 01:37:55
【问题描述】:

我正在尝试使用泛型来“泛化”一个实例化不同类型的网络传输的 var。我不确定“generic=no RTTI”规则是否会使该方法无效,因为我还没有跟上泛型的速度。

我读过这篇文章:

What is the correct way to structure this generic object creation ,在问题中陈述了以下内容:

如果可能的话,我想做的另一件事是改变两个 创作:

LAdapter := TSQLiteNativeConnectionAdapter.Create(LFilename)

LAdapter := TFireDacConnectionAdapter.Create(FDatabaseLink.FConnection as TFDConnection, FDatabaseLink.OwnedComponent)

在父级中使用抽象的“GetAdapterClass”类型函数 TModelDatabaseConnection 并在 孩子做类似的事情:

 LAdapter := GetAdapterClass.Create...

这正是我想做的。所以如果你能想象一下:

type
  TTransport<T> = class(TComponent)
  private
    ...
    function GetTransport: TTransport;
    procedure SetTransport(AValue: TTransport);
    ...
  public
    ...
    property Transport: TTransport read GetTransport write SetTransport;
    ...
  end;

  TTCPIPTransport = class(TTransport<T>)
  private
    function GetSocket(Index: Integer): String;
    procedure SetSocket(Index: Integer; AValue: String);
  public
    property Socket[Index: Integer]: String read GetSocket write SetSocket;
  end;

  TServiceTransport = class(TTransport<T>)
  private
    function GetServiceName: String;
    procedure SetServiceName(AValue: String);
  public
    property ServiceName: String read GetServiceName write SetServiceName;
  end;

  TISAPITransport = class(TServiceTransport<T>);

  THTTPSysTransport = class(TServiceTransport<T>)
  private
    function GetURL(Index: Integer): String;
    procedure SetURL(Index: Integer; AValue: String);
  public
    property URL[Index: Integer]: read GetURL write SetURL;
  end;

  etc.

这个想法是创建一个基类,其中包含所有传输通用的所有字段/属性/方法,然后具有包含仅对特定传输子集通用的字段/方法/属性的中间类,然后具有每个传输的最终版本都特定于类型。

所以当我打电话时:

var
  trans: TTransport<T> // or TTransport<TTCPIPTransport> etc.
begin
  trans := TTransport<TTCPIPTransport>.Create(AOwner,....);
  trans.Transport.Socket[0] := '127.0.0.1:8523';
          OR
  trans := TTransport<TISAPITransport>.Create(AOwner,...);
  trans.Transport.ServiceName = 'Foo';
  ...
  etc.
end;

或者可能比那个更通用,但是让每个 trans 实例(没有类型转换)自动显示特定于子类的属性/字段/方法。

这样我可以有一个配置屏幕,允许管理员在组合框中选择传输类型,让该变量值在代码中设置 内的类型,并且一组代码处理创建对象的类型。

这可以使用泛型吗?

【问题讨论】:

  • 这里没有泛型的情况,至少从你给出的例子来看。你的TTCPIPTransport已经是(一个更专业的版本)TTransport,这是简单的继承/多态,不需要任何T。泛型的用途是当您想要在方法的参数类型、方法的返回类型、属性类型或同一泛型类( 的类)内的字段类型之间施加编译器强制类型约束。
  • 泛型是一个编译时构造。您想在运行时选择类型。
  • 对不起,我不是很熟悉。我想实例化一个泛型基类,并通过指定我想要的类型来获得更具体的类型,而不必对泛型基类实例进行类型转换,并且特定类的字段/属性/方法存在于基类变量中实例。
  • 泛型不是解决方案。您要查找的搜索表达式是 class factory
  • 您的问题完全可以解决,而且很容易解决。试图强制泛型进入运行时类型变化只是一个错误。因为泛型是一个编译时构造。

标签: oop delphi generics inheritance


【解决方案1】:

这是我在类工厂的第一次(微弱的)尝试,以前从未这样做过。它部分工作(生成正确的类),但不能作为基类的不同子类访问,而不进行类型转换,这违背了目的。请参阅内联 cmets

TWSTransport = class(TComponent)
  ...
public
  constructor Create(AOwner: TComponent); virtual; 
  ....   
end;

TWSTransportClass = Class of TWSTransport;

TWSTCPIPTransportClass = class of TWSTCPIPTransport;

TWSHTTPSysTransport = class(TWSServiceTransport);

TWSServiceTransport = class(TWSTransport);

TWSTransportStringConversion = class(TWSTransport);

TWSTransportStreamFormat = class(TWSTransportStringConversion);

TTransportFactory = class(TClassList)
private
  function GetTransport(Index: TWSTransportClass; AOwner: TkbmMWServer): TWSTransportClass;
public
  procedure RegisterTransportClass(ATransportClass: TWSTransportClass);
  property Transport[Index: TWSTransportClass; AOwner: TkbmMWServer]: TWSTransportClass read GetTransport;
end;    

function TTransportFactory.GetTransport(Index: TWSTransportClass; AOwner: TkbmMWServer): TWSTransportClass;
begin
if IndexOf(Index) > -1 then
  Result := TWSTransportClass(Items[IndexOf(Index)])
else
  Result := TWSTransportClass(Index.Create(AOwner));
end;

procedure TTransportFactory.RegisterTransportClass(ATransportClass: TWSTransportClass);
var
  index: Integer;
begin
  // is the transport registered?
  index := IndexOf(ATransportClass);
  if index < 0 then
    // the transport is not registered, add it to the list
    Add(ATransportClass);
end;



initialization
  factory := TTransportFactory.Create;
  factory.RegisterTransportClass(TWSHTTPSysTransport);
  factory.RegisterTransportClass(TWSISAPIRESTTransport);
  factory.RegisterTransportClass(TWSTCPIPTransport);

finalization
  FreeAndNil(factory);

end.

这是我的测试方法:

procedure TForm4.FormCreate(Sender: TObject);
var
  //trans: TWSTCPIPTransport; // this doesn't work
  trans: TWSTransport; // this works
begin
  trans := factory.Transport[TWSTCPIPTransport,Self];
  showmessage(trans.classname); // this shows the correct classname - TWSTCPIPTransport
  trans.AddSocket('127.0.0.1:80'); // the compiler gives an error here because this call is specific to a subclass of TWSTransport, TWSTCPIPTransport.
end;

所以我仍然遗漏了一些东西......有人看到错误了吗?

【讨论】:

  • 你应该解释为什么你发现泛型不合适,解释你发现了什么,并解释为什么这个代码是一个更好的选择。只是将代码转储到答案空间中并不能在这里真正起作用。
  • 其实泛型并不是不合适的;我找到了一个使用泛型来完成任务的项目。然而,在我的研究中,我也开始意识到 D 如何在它自己的内部类工厂机制中使用 TComponentClass 等东西,并认为这是一种更好的方法,因为它避免了泛型的开销。但是,我还没有实现让工厂返回我的子类并且能够将其分配给子类实例而无需类型转换的目标。 :-(
  • 因为有人设法做到这一点并不适合。我很确定,在历史上的某个地方,有人设法用锤子敲了一个螺丝钉,让它把东西固定在适当的位置。这并不能使锤子适合代替螺丝刀。 :-)
猜你喜欢
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-19
  • 2018-04-19
  • 2017-01-01
相关资源
最近更新 更多