delphi - 如何在没有冗余列表的情况下迭代父/子层次结构中的对象?

标签 delphi hierarchy tobjectlist

我有一个像这样的对象......

type
  TMyObject = class(TObject)
  private
    FParent: TMyObject;
    FChildren: TObjectList<TMyObject>;
    function GetChildren(const Index: Integer): TMyObject;
  public
    constructor Create(AParent: TMyObject);
    destructor Destroy; override;
    function AddChild: TMyObject;
    procedure DeleteChild(const Index: Integer);
    function ChildCount: Integer;
    property Children[const Index: Integer]: TMyObject read GetChildren; default;
  end;

(还有更多,但这是基本思想)

这允许对象之间建立简单的父/子关系,即层次结构。一是根,它包含更多的层次结构。

这一切都很好,除了我还需要迭代所有这些对象的完整列表,无论层次结构如何。

var
  Node: TMyObject;
for X := 0 to AllNodes.Count-1 do begin
  Node := AllNodes[X];
  //Do something with `Node`... 

end;

当然,我可以创建一个对象列表并同时维护它们......

FAllObjects: TObjectList<TMyObject>;

但是,这是多余的。 TMyObject 的每个实例都必须同时添加/删除到每个结构中。我想摆脱对一个主列表的要求,而只使用层次结构对象。但我不知道如何在不遵循递归层次结构的情况下迭代所有对象。例如,像获取所有这些项目的总数这样简单的事情。

如何维护这样一个对象层次结构(我可以在一个循环中迭代所有项目)而不必维护两个单独的冗余结构?

例如,TTreeView.Items 具有我想要的行为。您可以使用 Items.CountItems[Index] 迭代所有 项,也可以递归迭代树层次结构。

最佳答案

我会以功能性的方式解决这个问题,而不需要修改(*)您想要遍历的结构/类。

您需要的是根项和获取项子项的函数。

(*)关于您的TMyObject类需要以某种方式公开子级(我会通过放置 property Childs: TEnumerable<TMyObject> 以使它们只读来做到这一点。

以下是如何预先排序遍历基本上任何非多态层次结构:

unit HierarchyEnumerator;

interface

uses
  Generics.Collections,
  SysUtils;

type
  THierarchyEnumerable<T> = record
  private
    fItems: TEnumerable<T>;
    fChildSelector: TFunc<T, TEnumerable<T>>;

    type
      TEnumerator = class
      private
        fStack: TStack<TEnumerator<T>>;
        fChildSelector: TFunc<T, TEnumerable<T>>;
        fCurrent: T;
      public
        constructor Create(const items: TEnumerable<T>; const childSelector: TFunc<T, TEnumerable<T>>);
        destructor Destroy; override;
        function MoveNext: Boolean;
        property Current: T read fCurrent;
      end;
  public
    constructor Create(const items: TEnumerable<T>; const childSelector: TFunc<T, TEnumerable<T>>);
    function GetEnumerator: TEnumerator;
  end;

implementation

{ THierarchyEnumerable<T> }

constructor THierarchyEnumerable<T>.Create(const items: TEnumerable<T>;
  const childSelector: TFunc<T, TEnumerable<T>>);
begin
  fItems := items;
  fChildSelector := childSelector;
end;

function THierarchyEnumerable<T>.GetEnumerator: TEnumerator;
begin
  Result := TEnumerator.Create(fitems, fChildSelector);
end;

{ THierarchyEnumerable<T>.TEnumerator }

constructor THierarchyEnumerable<T>.TEnumerator.Create(const items: TEnumerable<T>;
  const childSelector: TFunc<T, TEnumerable<T>>);
var
  item: T;
begin
  inherited Create;
  fStack := TStack<TEnumerator<T>>.Create;
  fStack.Push(items.GetEnumerator);
  fChildSelector := childSelector;
end;

destructor THierarchyEnumerable<T>.TEnumerator.Destroy;
begin
  fStack.Free;
  inherited;
end;

function THierarchyEnumerable<T>.TEnumerator.MoveNext: Boolean;
var
  e: TEnumerator<T>;
begin
  while fStack.Count > 0 do
  begin
    e := fStack.Pop;
    if e.MoveNext then
    begin
      fStack.Push(e);
      fCurrent := e.Current;
      fStack.Push(fChildSelector(fCurrent).GetEnumerator);
      Exit(True);
    end
    else
      e.Free;
  end;

  Result := False;
end;

end.

使用看起来像这样:

for o in THierarchyEnumerable<TMyObject>.Create(list,
  function(item: TMyObject): TEnumerable<TMyObject>
  begin
    Result := item.Children;
  end) do
  ...

关于delphi - 如何在没有冗余列表的情况下迭代父/子层次结构中的对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44144504/

相关文章:

delphi - H2161 警告 : Duplicate resource: Type 10 (RCDATA)

c# - 如何在 FK 指向同一表的 PK 的表上的 SQL 中级联删除?

Delphi:如何使用 TObjectList<T>?

delphi - 无法为 TObjectList 调用 Binarysearch 函数

android - 为以编程方式拍摄的照片禁用自动保存?

Delphi 应用程序.HintColor

ruby-on-rails - rails : Multiple trees for a single item

delphi - Delphi中exe和DLL之间的TobjectList

Delphi 将字符串参数传递给 IDispatch.Invoke

android - 在哪里可以找到 Android Studio 路径?