DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: forget66
今日帖子: 65
在线用户: 12
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2018/9/30 17:50:43
标题:
请教delphi泛型的问题,如何动态引用? 浏览:1627
加入我的收藏
楼主: 我有一个类
  TDoc = class...
它有很多子类
  TDoc1, TDoc2, TDoc3...

又有一个泛型List
  TDocList<T : TDoc> = class...

然后我需要根据需求创建不同的List
  case DocType of
    1 : lst := TDocList<TDoc1>.create;
    2 : lst := TDocList<TDoc2>.create;
    ...
  end;

这样扩展起来很麻烦,于是,定义一个Class
  TDocClass = class of TDoc;
和一个函数,可以返回数值对应的DocClass
  GetDocClass(atype : integer) : TDocClass;

如何根据一个TDocClass创建一个对应的TDocList呢?
  lst := TDocList<TDocClass??>.Create...
或者有其它能动态指定T的方法?
----------------------------------------------
-
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/9/30 18:31:53
1楼: var
  T: TDocClass;
  A: TDoc;
begin
  A := T.Create;
end;
----------------------------------------------
(C)(P)Flying Wang
作者:
男 bahamut8348 (leonna) ★☆☆☆☆ -
普通会员
2018/9/30 23:29:33
2楼: 父类子对象就可以

type
tparent = class
...

type
tchild = class tparent
...


调用类似:
var
  c: tparent;
begin
  c := tchild.create(...);

或者:
var
  c: tparent;
begin
  c = tchild.newinstance();
  c.create(...);
----------------------------------------------
--
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2018/10/3 23:27:02
3楼: 回上面两位:
类我知道,这里是泛型
----------------------------------------------
-
作者:
男 bamboocaep (bamboocaep) ★☆☆☆☆ -
普通会员
2018/10/5 7:35:42
4楼: TDocList的create方法传入一个tdocclass作为参数
----------------------------------------------
-
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2018/10/8 9:05:16
5楼: 看来是行不通了
----------------------------------------------
-
作者:
男 drroc (mvcxe) ★☆☆☆☆ -
盒子活跃会员
2018/10/8 10:44:34
6楼: 理解不了你的需求,一般来说,rtti动态引用都要通过TValue,你可以先去了解一下TValue
----------------------------------------------
MVCXE中国首个DELPHI MVC WEB框架:https://www.mvcxe.com/
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2018/10/8 17:29:50
7楼: 你的写法不对,你写了TDocList<T : TDoc> = class...之后TDocList容器只能定义为保存TDoc子类类型的容器,那么就不能TDocList<TDocClass??>,你是想用TDocClass的变量来创建实例的话编译器就不能在定义时知道你变量的实际类型,那就不能定义。
目前不能做到你这种使用方法,但有很多方法能间接的达到和你想达到的目标一致的效果。
----------------------------------------------
-
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/10/8 19:03:08
8楼: 楼主连需求都不打算告诉你。
所以,你的间接,也不一定是他想要的。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2018/10/8 21:53:54
9楼: 好吧,有一个思路。
可以在TDocList<T : TDoc>类型里写上一些泛型函数,函数调用元素实际类型的相关方法,在后面实做元素实际类型时重载这些方法,这样你就不需要再容器内试图一步到位的处理实际类型达到特殊目的了。
----------------------------------------------
-
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2018/10/9 9:53:36
10楼: 既然TDoc1, TDoc2, TDoc3是TDoc的子类 你干嘛还要纠结TDocList<TDoc1> TDocList<TDoc2> 之类的 只用一个TDocList<TDoc>不就可以了么, list里实际数据才是子类的实例啊
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 wang_80919 (Flying Wang) ★☆☆☆☆ -
普通会员
2018/10/9 10:04:03
11楼: 楼上虽然正解。但是楼主大概就是纠结这个。
----------------------------------------------
(C)(P)Flying Wang
作者:
男 tiez (骑牛夜旅) ★☆☆☆☆ -
普通会员
2018/10/9 10:40:40
12楼: 从楼主希望使用元类变量来看,可能是他想预先做好容器,在后面的开发中可以存放不同的后续开发的类,这些类还可以通过元类变量或容器本身来实例化、管理等等
----------------------------------------------
-
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2018/10/11 14:37:36
13楼: 我把全部示例代码贴一下吧

unit uBaseRecord;

interface

uses
  SysUtils, Classes, Generics.Collections;

type
  TRecordType = type Integer;

  TRecord = class;
  TRecordClass = class of TRecord;
  TRecordList<T : TRecord> = class;

  TRecord = class
  private
    FChildsDict : TDictionary<TRecordType, TRecordList<TRecord>>;
    FParent: TRecordList<TRecord>;
    F_ID: Integer;
    F_Index: Double;
    F_PID: Integer;
    procedure Set_Index(const Value: Double);
  protected
    class function GetRecordType: TRecordType; virtual;
    class procedure RegistryClass;
  public
    class function GetClass(ARecordType : TRecordType) : TRecordClass;
    constructor Create(AParent: TRecordList<TRecord>); reintroduce; virtual;
    destructor Destroy; override;
    function Childs<T : TRecord> : TRecordList<T>; overload;
    function Childs(ARecordType : TRecordType) : TRecordList<TRecord>; overload;
    procedure Post;
    property Parent: TRecordList<TRecord> read FParent;
    property _ID: Integer read F_ID;
    property _Index: Double read F_Index write Set_Index;
    property _PID: Integer read F_PID;
    property _Type: TRecordType read GetRecordType;
  end;

  TRecordList<T : TRecord> = class
  private
    FCount: Integer;
    FItems : TList<T>;
    FParent: TRecord;
    function GetItems(Index: Integer): T;
    function GetRecordType: TRecordType;
  public
    constructor Create(AParent: TRecord); reintroduce; virtual;
    destructor Destroy; override;
    function Add: T;
    procedure Clear;
    procedure Delete(AIndex: Integer);
    procedure Remove(AItem: T);
    class procedure Test;
    property Count: Integer read FCount;
    property Items[Index: Integer]: T read GetItems;
    property Parent: TRecord read FParent;
    property RecordType: TRecordType read GetRecordType;
  end;

type
  TRecord1 = class(TRecord)
  protected
    class function GetRecordType: TRecordType; override;
  end;
  TRecord2 = class(TRecord)
  protected
    class function GetRecordType: TRecordType; override;
  end;

implementation

var
  G_RecordClassDict : TDictionary<TRecordType, TRecordClass>;

constructor TRecord.Create(AParent: TRecordList<TRecord>);
begin
  inherited Create;
  FParent :=  AParent;
  FChildsDict := TDictionary<TRecordType, TRecordList<TRecord>>.Create;
end;

destructor TRecord.Destroy;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
begin
  for pair in FChildsDict do
  begin
    pair.Value.FParent :=  nil;
    pair.Value.Free;
  end;
  FChildsDict.Free;
  inherited;
end;

function TRecord.Childs(ARecordType: TRecordType): TRecordList<TRecord>;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
  cls : TRecordClass;
begin
  for pair in FChildsDict do
    if pair.Key = ARecordType then
      Exit(pair.Value);
  cls :=  TRecord.GetClass(ARecordType);
  if Assigned(cls) then
  begin
    //问题所在,如何创建?  Result  :=  TRecordList<cls>.Create(Self) as TRecordList<TRecord>;
    FChildsDict.Add(Result.RecordType, Result);
  end
  else
    Result  :=  nil;
end;

function TRecord.Childs<T>: TRecordList<T>;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
begin
  for pair in FChildsDict do
    if pair.Key = T.GetRecordType then
      Exit(TRecordList<T>(pair.Value));
  Result  :=  TRecordList<T>.Create(Self);
  FChildsDict.Add(Result.RecordType, TRecordList<TRecord>(Result));
end;

class function TRecord.GetClass(ARecordType: TRecordType): TRecordClass;
begin
  if not G_RecordClassDict.TryGetValue(ARecordType, Result) then
    Result  :=  nil;
end;

class function TRecord.GetRecordType: TRecordType;
begin
  Result := 0;
end;

procedure TRecord.Post;
begin
  // TODO -cMM: TRecord.Post default body inserted
end;

class procedure TRecord.RegistryClass;
begin
  if G_RecordClassDict.ContainsKey(Self.GetRecordType) then
    raise Exception.Create('重复注册.');
  G_RecordClassDict.Add(Self.GetRecordType, Self);
end;

procedure TRecord.Set_Index(const Value: Double);
begin
  F_Index := Value;
end;

constructor TRecordList<T>.Create(AParent: TRecord);
begin
  inherited Create;
  FParent :=  AParent;
  FItems :=  TList<T>.Create;
end;

destructor TRecordList<T>.Destroy;
begin
  Clear;
  FItems.Free;
  inherited;
end;

function TRecordList<T>.Add: T;
begin
  Result := TRecordClass(Pointer(T)).Create(TRecordList<TRecord>(Self)) as T;
  FItems.Add(Result);
end;

procedure TRecordList<T>.Clear;
var
  item : T;
begin
  for item in FItems do
    item.Free;
  FItems.Clear;
end;

procedure TRecordList<T>.Delete(AIndex: Integer);
var
  item : T;
begin
  item  :=  FItems[AIndex];
  Remove(item);
end;

function TRecordList<T>.GetItems(Index: Integer): T;
begin
  Result := FItems[Index];
end;

function TRecordList<T>.GetRecordType: TRecordType;
begin
  Result  :=   T.GetRecordType;
end;

procedure TRecordList<T>.Remove(AItem: T);
begin
  FItems.Remove(AItem);
  AItem.FParent :=  nil;
  AItem.Free;
end;

class procedure TRecordList<T>.Test;
var
  item : TRecord;
  clist : TRecordList<TRecord2>;
  cItem : TRecord2;
  cls : TRecordClass;
  ditem : TRecord;
begin
  with Self.Create(nil) do
  try
    item  :=  Add;
    clist :=  item.Childs<TRecord2>;
    citem :=  clist.Add;
    cls :=  cItem.GetClass(2);
    if Assigned(cls) then
    begin
      //citem.Childs<T(Pointer(cls)^)>;
    end;
  finally
    Free;
  end;
end;

{ TRecord1 }

class function TRecord1.GetRecordType: TRecordType;
begin
  Result  :=  1;
end;

{ TRecord2 }

class function TRecord2.GetRecordType: TRecordType;
begin
  Result  :=  2;
end;

initialization
  G_RecordClassDict := TDictionary<TRecordType, TRecordClass>.Create;
  TRecord1.RegistryClass;
  TRecord2.RegistryClass;
  TRecordList<TRecord1>.Test;

finalization
  G_RecordClassDict.Free;

end.


想法是这样的:
  数据库中存储一个树,每个节点下都可能有n种类型的子节点
  节点有个属性_Type,记录了节点的类型,每个类型对应程序中的不同类实例
  程序可以根据node.Childs<TDoc1>取出TDoc1类型的子节点,以此类推
  显然,doc1和doc2有完全不同的属性,只是存储时是树结构,类似xml文档

使用时,从数据库中读出一个record,它的_Type为1,对应程序中的类为TDoc1
然后读它的所有子节点,例如读到一个_Type为2的节点,对应程序中的类为TDoc2
此时要创建一个TRecordList<TDoc2>的实例并将doc2加进去
根据_Type=x获得TDocX类然后实例化很简单

问题是: 如何实例化TRecordList<TDocX>?
此时的X是不确定的,因为TDocX是会不断扩充的
----------------------------------------------
-
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2018/10/11 15:32:32
14楼: 看来泛型必须在编译期知道类型,而不能在运行期动态使用
只能绕个弯,添加一个CreateList类方法实现了

unit uBaseRecord;

interface

uses
  SysUtils, Classes, Generics.Collections, Windows;

type
  TRecordType = type Integer;

  TRecord = class;
  TRecordClass = class of TRecord;
  TRecordList<T : TRecord> = class;

  TRecord = class
  private
    FChildsDict : TDictionary<TRecordType, TRecordList<TRecord>>;
    FParent: TRecordList<TRecord>;
    F_ID: Integer;
    F_Index: Double;
    F_PID: Integer;
    procedure Set_Index(const Value: Double);
  protected
    class function CreateList(AParent: TRecord): TRecordList<TRecord>; virtual; abstract;
    class function GetRecordType: TRecordType; virtual; abstract;
    class procedure RegistryClass;
  public
    constructor Create(AParent: TRecordList<TRecord>); reintroduce; virtual;
    destructor Destroy; override;
    function Childs(ARecordType : TRecordType) : TRecordList<TRecord>; overload;
    function Childs<T : TRecord> : TRecordList<T>; overload;
    class function GetClass(ARecordType : TRecordType) : TRecordClass;
    procedure Post;
    property Parent: TRecordList<TRecord> read FParent;
    property _ID: Integer read F_ID;
    property _Index: Double read F_Index write Set_Index;
    property _PID: Integer read F_PID;
    property _Type: TRecordType read GetRecordType;
  end;

  TRecordList<T : TRecord> = class
  private
    FItems : TList<T>;
    FParent: TRecord;
    function GetCount: Integer;
    function GetItems(Index: Integer): T;
    function GetRecordType: TRecordType;
  public
    constructor Create(AParent: TRecord); reintroduce; virtual;
    destructor Destroy; override;
    function Add: T; virtual; //必须virtual/dynamic,否则子类list强转再add会有问题
    procedure Clear;
    procedure Delete(AIndex: Integer);
    procedure Remove(AItem: T);
    class procedure Test;
    property Count: Integer read GetCount;
    property Items[Index: Integer]: T read GetItems;
    property Parent: TRecord read FParent;
    property RecordType: TRecordType read GetRecordType;
  end;

type
  TRecord1 = class(TRecord)
  protected
    class function CreateList(AParent: TRecord) : TRecordList<TRecord>; override;
    class function GetRecordType: TRecordType; override;
  end;
  TRecord2 = class(TRecord)
  protected
    class function CreateList(AParent: TRecord) : TRecordList<TRecord>; override;
    class function GetRecordType: TRecordType; override;
  end;

implementation

var
  G_RecordClassDict : TDictionary<TRecordType, TRecordClass>;

constructor TRecord.Create(AParent: TRecordList<TRecord>);
begin
  inherited Create;
  FParent :=  AParent;
  FChildsDict := TDictionary<TRecordType, TRecordList<TRecord>>.Create;
end;

destructor TRecord.Destroy;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
begin
  for pair in FChildsDict do
  begin
    pair.Value.FParent :=  nil;
    pair.Value.Free;
  end;
  FChildsDict.Free;
  inherited;
end;

function TRecord.Childs(ARecordType: TRecordType): TRecordList<TRecord>;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
  cls : TRecordClass;
begin
  for pair in FChildsDict do
    if pair.Key = ARecordType then
      Exit(pair.Value);
  cls :=  TRecord.GetClass(ARecordType);
  if Assigned(cls) then
  begin
    Result  :=  cls.CreateList(Self);
    FChildsDict.Add(ARecordType, Result);
  end
  else
    Result  :=  nil;
end;

function TRecord.Childs<T>: TRecordList<T>;
var
  pair : TPair<TRecordType, TRecordList<TRecord>>;
begin
  for pair in FChildsDict do
    if pair.Key = T.GetRecordType then
      Exit(TRecordList<T>(pair.Value));
  Result  :=  TRecordList<T>.Create(Self);
  FChildsDict.Add(Result.RecordType, TRecordList<TRecord>(Result));
end;

class function TRecord.GetClass(ARecordType: TRecordType): TRecordClass;
begin
  if not G_RecordClassDict.TryGetValue(ARecordType, Result) then
    Result  :=  nil;
end;

procedure TRecord.Post;
begin
  // TODO -cMM: TRecord.Post default body inserted
end;

class procedure TRecord.RegistryClass;
begin
  if G_RecordClassDict.ContainsKey(Self.GetRecordType) then
    raise Exception.Create('重复注册.');
  G_RecordClassDict.Add(Self.GetRecordType, Self);
end;

procedure TRecord.Set_Index(const Value: Double);
begin
  F_Index := Value;
end;

constructor TRecordList<T>.Create(AParent: TRecord);
begin
  inherited Create;
  FParent :=  AParent;
  FItems :=  TList<T>.Create;
end;

destructor TRecordList<T>.Destroy;
begin
  Clear;
  FItems.Free;
  inherited;
end;

function TRecordList<T>.Add: T;
begin
  Result := TRecordClass(Pointer(T)).Create(TRecordList<TRecord>(Self)) as T;
  FItems.Add(Result);
end;

procedure TRecordList<T>.Clear;
var
  item : T;
begin
  for item in FItems do
    item.Free;
  FItems.Clear;
end;

procedure TRecordList<T>.Delete(AIndex: Integer);
var
  item : T;
begin
  item  :=  FItems[AIndex];
  Remove(item);
end;

function TRecordList<T>.GetCount: Integer;
begin
  Result  :=  FItems.Count;
end;

function TRecordList<T>.GetItems(Index: Integer): T;
begin
  Result := FItems[Index];
end;

function TRecordList<T>.GetRecordType: TRecordType;
begin
  Result  :=   T.GetRecordType;
end;

procedure TRecordList<T>.Remove(AItem: T);
begin
  FItems.Remove(AItem);
  AItem.FParent :=  nil;
  AItem.Free;
end;

class procedure TRecordList<T>.Test;
var
  item : TRecord;
  clist : TRecordList<TRecord1>;
  cItem : TRecord1;
  dlist : TRecordList<TRecord2>;
  ditem : TRecord2;
  elist : TRecordList<TRecord>;
  eitem : TRecord;
begin
  with Self.Create(nil) do
  try
    item  :=  Add;
    Writeln(Self.ClassName);  //TRecordList<uBaseRecord.TRecord>
    Writeln(item.ClassName);  //TRecord

    clist :=  item.Childs<TRecord1>;
    citem :=  clist.Add;
    Writeln(clist.ClassName); //TRecordList<uBaseRecord.TRecord1>
    Writeln(citem.ClassName); //TRecord1

    dlist :=  TRecordList<TRecord2>(cItem.Childs(2));
    if Assigned(dlist) then
    begin
      ditem :=  dlist.Add;
      Writeln(dlist.ClassName); //TRecordList<uBaseRecord.TRecord2>
      Writeln(ditem.ClassName); //TRecord2
    end;

    elist :=  cItem.Childs(2);
    if Assigned(elist) then
    begin
      eitem :=  elist.Add;
      Writeln(elist.ClassName); //TRecordList<uBaseRecord.TRecord2>
      Writeln(eitem.ClassName); //TRecord  ??
    end;
  finally
    Free;
  end;
end;

{ TRecord1 }

class function TRecord1.CreateList(AParent: TRecord): TRecordList<TRecord>;
begin
  Result  :=  TRecordList<TRecord>(TRecordList<TRecord1>.Create(AParent));
end;

class function TRecord1.GetRecordType: TRecordType;
begin
  Result  :=  1;
end;

{ TRecord2 }

class function TRecord2.CreateList(AParent: TRecord): TRecordList<TRecord>;
begin
  Result  :=  TRecordList<TRecord>(TRecordList<TRecord2>.Create(AParent));
end;

class function TRecord2.GetRecordType: TRecordType;
begin
  Result  :=  2;
end;

initialization
  G_RecordClassDict := TDictionary<TRecordType, TRecordClass>.Create;
  TRecord1.RegistryClass;
  TRecord2.RegistryClass;
  TRecordList<TRecord>.Test;

finalization
  G_RecordClassDict.Free;

end.
----------------------------------------------
-
作者:
男 iamdream (银河恒久远,梦想无止境!) ★☆☆☆☆ -
大贡献会员
2018/10/11 21:25:27
15楼: Delphi的泛型和C++里的模板(template)是一样的东西,都是为编译服务的,你想在运行期动态应用,不能用泛型,只能用类什么的。
----------------------------------------------
-广袤璀璨的银河,永无止境的梦想(梦无止境游银河) 博客挂了……
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2018/10/12 9:00:24
16楼: 嗯 楼主终于悟到了  泛型是给编译期使用的东西, 编译后就没泛型这玩意了

再说了, 你想想, 假设你真的能创建出来TRecordList<TDocX>这样的东西, 那你相关的业务代码怎么处理? 难道再做类型判断?
如果是每个TDocX自己执行业务代码, 父类提供统一方法名, 那和创建TRecordList<TDoc>又有什么区别
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 hdcopy (hdcopy) ★☆☆☆☆ -
普通会员
2018/10/12 9:28:58
17楼: 为啥不用类?
----------------------------------------------
-
作者:
男 hexi (Hexi) ★☆☆☆☆ -
盒子活跃会员
2018/10/21 20:25:22
18楼: 你需要的功能可以用Spring4D框架的容器来实现。
先通过字符串注册类和接口到容器中
然后可通过字符串,来构造关联的类的实体,并可返回接口
Spring4D还支持依赖注入,反向注入

例如注册一个类T,实现了接口ITest
Container.RegisterType<T>.Implements<ITest>(‘注册字符串名称’);
构造:
var 
  R:ITest;
R:=ServiceLocator.GetService<ITest>(‘注册字符串名称’)

此时创建了一个T的实体,同时返回实体实现的ITest接口给R
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行93.75毫秒 RSS