【问题标题】:How to a) Iterate over Delphi (Seattle) TObjectList<T> and/or TList<T> objects and access an object at an index如何 a) 遍历 Delphi (Seattle) TObjectList<T> 和/或 TList<T> 对象并访问索引处的对象
【发布时间】:2016-08-31 10:39:01
【问题描述】:

我正在创建一个 Datasnap 服务器和客户端集,最终用于连接(服务器)到几个不同的数据库。对于服务器,我创建了一个名为 TMember 的测试类。服务器和客户端使用这个单元(uMember)。

我在服务器上放置了一个函数,该函数创建一个 TMember 并将其返回给请求的客户端机器。 TMember 对象中的一些数据是从 SQL Server 数据库中收集的。

在客户端,我创建了一个 TObjectList 类型的全局变量(在我的客户端主窗体上)。每次我触发 Create New Member 函数时,对象都会在服务器上创建并成功发送回客户端。我可以访问对象的方法和属性。

然而,我的问题是遍历这个 TObjectList 集合。无论我采用哪种方式,我都只会获得 AV。这是我的代码尝试。谁能指出我在哪里搞砸了?

非常感谢您!

TMember界面如下:

    type
  TMember = class
    FFirstName : String;
    FLastName : String;
    FFullname : String;
    FAddress : String;
  private
    {private declarations}
  public
    {public declarations and properties}
    constructor Create(fname, lname : String); overload;
    constructor Create(otherMember : TMember); overload;
    destructor Destroy;
    procedure SetFName(const Value: String);
    procedure SetFullname(const Value: String);
    procedure SetLName(const Value: String);
    function GetFullname : String;
    function ToString : String;
    procedure ChangeAddr(Addr : String);

    property Name : String read FFirstName write SetFName;
    property Surname : String read FLastName write SetLName;
    property Fullname : String read GetFullname write SetFullname;
  end;

此代码用于从服务器创建一个新的 TMember 对象:

    procedure TfrmMainClient.btnCreateMemClick(Sender: TObject);
var
  Server : TServerMethodsClient;
  newMem : TMember;
  i : Integer;
begin
  try
    Server := TServerMethodsClient.Create(DM1.ServerConn.DBXConnection);
    newMem := Server.GetMember(edtFName.Text, '');
    if Assigned(newMem) then begin
      AllMembers.Add(newMem);
      Listbox1.Items.Add(newMem.GetFullname);
    end;

  finally
    Server.Free;
  end;
end;

因此,一段时间后,客户端的内存中会出现几个对象。现在我想从列表框中访问它们(好像它是一个“菜单”来选择一个 TMember)。这个想法是/是记录列表框的 ItemIndex 并将其发送到 AllMember TObjectList 对象集合以访问位于该索引处的 TMember。这创造了一个可爱的小 AV。所以,然后我尝试使用基本的 for..do 循环对 ObjectList 进行简单的迭代,以填充 Listbox。结果相同。我也尝试了 for..in 循环,但无济于事。

我很确定我只是错过了一些愚蠢的事情......

这是尝试#1:

    procedure TfrmMainClient.btnRefreshClick(Sender: TObject);
var
  nextMember : TMember;
begin
  for nextMember in AllMembers do
    begin
      Listbox1.Items.Add(nextMember.GetFullname);
    end;
end;

这是尝试#2:

    procedure TfrmMainClient.btnRefreshClick(Sender: TObject);
var
  nextMember : TMember;
  i : Integer;
begin
  for i := 0 to AllMembers.Count - 1 do
    begin
      nextMEmber := TMember.Create(AllMembers[i]);
      Listbox1.Items.Add(nextMember.GetFullname);
    end;
end;

这是尝试#3:

    procedure TfrmMainClient.btnRefreshClick(Sender: TObject);
var
  nextMember : TMember;
  i : Integer;
begin
  for i := 0 to AllMembers.Count - 1 do
    begin
      nextMember := AllMembers.Items[i];
      Listbox1.Items.Add(nextMember.GetFullname);
    end;
end;

我相信有人可以提供帮助...

编辑 1:

改变这个

 procedure TfrmMainClient.btnRefreshClick(Sender: TObject);
var
  i: Integer;
  Item : TPair<Integer, TMember>;
  ActiveMember : TMember;
  Key : Integer;
begin
  Listbox1.Clear;
  for i := 0 to AllMembersKeys.Count - 1 do
    begin
      try
        ActiveMember := TMember.Create('','');

        //Get the MemberID as a Key stored in AllMembersKeys
        Key := AllMembersKeys[i];

        //Now try get this TMember record from the AllMembers collection
        if AllMembers.TryGetValue(Key, ActiveMember) then
          if ActiveMember <> nil then
            Listbox1.Items.Add(ActiveMember.GetFullname)
        else
          Listbox1.Items.Add('Could not locate TMember with Key: ' + IntToStr(Key));
      finally
        ActiveMember.Free;
      end;
    end;
end;

procedure TfrmMainClient.btnRefreshClick(Sender: TObject);
var
  Key : Integer;
begin
  Listbox1.Clear;
  for Key in AllMembers.Keys do
  begin
    Listbox1.Items.Add(AllMembers[Key].GetFullname);
  end;
end;

【问题讨论】:

  • 旁白:构造函数必须在尝试之前出现,否则构造函数中的异常会导致您在未初始化的引用上调用Free
  • 至于问题,有很多细节缺失。从GetMember 返回的对象。谁拥有它?谁负责摧毁它? AllMembers 是什么? TObjectListOwnsObjects 设置为 True。这听起来像是一个需要minimal reproducible example 的问题。
  • Server.GetMemeber() 是如何实现的?正如@David 所说,请提供一个 MCVE。否则有太多的猜测。
  • 调试你的程序。为您的类创建一个空的重写析构函数。在其上放置一个断点并运行您的代码。检查调用堆栈并查看析构函数何时以及由谁调用

标签: delphi datasnap delphi-10-seattle


【解决方案1】:

更改 AllMembers 的类型
TObjectList<TMember> 

TDictionary<Integer, TMember>

并使用以下逻辑将 TMember 对象添加到 TDictionary。然后你可以使用 itemindex 从 AllMembers 列表中获取成员,例如 AllMembers[itemindex]。

procedure TfrmMainClient.btnCreateMemClick(Sender: TObject);
var
  Server : TServerMethodsClient;
  newMem : TMember;
  Idx : Integer;
begin
  try
    Server := TServerMethodsClient.Create(DM1.ServerConn.DBXConnection);
    newMem := Server.GetMember(edtFName.Text, '');
    if Assigned(newMem) then begin
      Idx := Listbox1.Items.Add(newMem.GetFullname);
      AllMembers.Add(Idx, newMem);
    end;
  finally
    Server.Free;
  end;
end;

【讨论】:

  • 非常感谢您的帮助。它可以工作,但是当我尝试回顾性地遍历 TDictionary 以读回它当前包含的对象时,我仍然会遇到 AV。我尝试使用它自己的迭代器(TPair),但访问它的 TMember Key.Value 部分仍然会导致 AV。我怎样才能最好地做到这一点?或者,我应该回避完整的对象而是去记录?
  • 您能否将代码添加到您如何循环列表的问题中。以及您是如何创建列表的?什么时候释放列表?
  • 他们不允许我把代码放在这里,所以我会试着把它放在别处(虽然不知道在哪里)...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多