【问题标题】:How to make this code re-usable如何使此代码可重用
【发布时间】:2015-11-22 12:38:24
【问题描述】:

我有一种特定的方式来为 Virtual Treeview 构建节点(我几年前就做过这个例子,直到现在都没有理由改变它)。由于我在大约 150 个案例中使用了几乎相同的代码,因此我想尝试使其可重用并减少整体代码行数。

我在表单上附上了带有 2 个按钮和一个虚拟树视图的完整示例代码:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, VirtualTrees, Vcl.StdCtrls;

type

  rTreeData = record
    IndexInMyData: integer;
  end;

  tLine = record
    Level:integer;
    Txt:string;
    NodePointer: PVirtualNode;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    VTV: TVirtualStringTree;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;
  vArray:array of tLine;

implementation

{$R *.dfm}



procedure TForm1.Button1Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: ^rTreeData;
  i, j: integer;
begin
  SetLength(vArray,5);
  vArray[0].Level:=0; vArray[0].Txt:='One';
  vArray[1].Level:=1; vArray[1].Txt:='Two';
  vArray[2].Level:=1; vArray[2].Txt:='three';
  vArray[3].Level:=2; vArray[3].Txt:='Four';
  vArray[4].Level:=0; vArray[4].Txt:='Give';

  VTV.BeginUpdate;
  VTV.Clear;
  for i := Low(vArray) to High(vArray) do
  begin
     if i = 0 then
    begin
      Node := VTV.AddChild(nil);
      Data := VTV.GetNodeData(Node);
    end
    else
    begin
      if vArray[i].Level = 0 then
        Node := VTV.AddChild(nil)
      else if vArray[i].Level > vArray[i - 1].Level then
        Node := VTV.AddChild(Node)
      else if vArray[i].Level < vArray[i - 1].Level then
      begin
        Node := Node.Parent;
        for j := 1 to (vArray[i - 1].Level - vArray[i].Level) do
          Node := Node.Parent;
        Node := VTV.AddChild(Node);
      end
      else
      begin
        Node := Node.Parent;
        Node := VTV.AddChild(Node);
      end;

      Data := VTV.GetNodeData(Node);
    end;

    // Create link to your data record into VST node
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;
end;

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode; var vData:rTreeData):PVirtualNode;
var j:integer;
begin
   if vI = 0 then
    begin
      Result := vTV.AddChild(nil);
      vData := rTreeData(vTV.GetNodeData(Result)^);
    end
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;

      vData := rTreeData(vTV.GetNodeData(Result)^);
    end;
    vData.IndexInMyData := vI;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  Node: PVirtualNode;
  Data: ^rTreeData;

  i, j,vLevelPrev: integer;
begin
  SetLength(vArray,5);
  vArray[0].Level:=0; vArray[0].Txt:='One';
  vArray[1].Level:=1; vArray[1].Txt:='Two';
  vArray[2].Level:=1; vArray[2].Txt:='three';
  vArray[3].Level:=2; vArray[3].Txt:='Four';
  vArray[4].Level:=0; vArray[4].Txt:='Give';

  VTV.BeginUpdate;
  VTV.Clear;
  for i := Low(vArray) to High(vArray) do
  begin
    if i = 0 then
      vLevelPrev:=0
    else
      vLevelPrev:=vArray[i-1].Level;

      Node:=AddNode(VTV,i,vArray[i].Level,vLevelPrev,Node,rTreeData(Data^));

//     if i = 0 then
//    begin
//      Node := VTV.AddChild(nil);
//      Data := VTV.GetNodeData(Node);
//    end
//    else
//    begin
//      if vArray[i].Level = 0 then
//        Node := VTV.AddChild(nil)
//      else if vArray[i].Level > vArray[i - 1].Level then
//        Node := VTV.AddChild(Node)
//      else if vArray[i].Level < vArray[i - 1].Level then
//      begin
//        Node := Node.Parent;
//        for j := 1 to (vArray[i - 1].Level - vArray[i].Level) do
//          Node := Node.Parent;
//        Node := VTV.AddChild(Node);
//      end
//      else
//      begin
//        Node := Node.Parent;
//        Node := VTV.AddChild(Node);
//      end;
//
//      Data := VTV.GetNodeData(Node);
//    end;

    // Create link to your data record into VST node
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;
end;

end.

所以,Button1 使用我当前的代码风格来构建节点。它确实有效。

Button2 正在尝试调用 AddNode 过程以将 AddNode 创建为可重用代码。 它可以编译,但在第 # 行失败:101: EAccessViolation:

我认为我的使用方式、分配指针和值存在问题...我没有越过这一行,所以我不知道其余代码是否有效。

任何建议如何解决这个问题,如何使代码可重用?

编辑

如果我删除 vData 参数效果很好: 现在这是简化的代码:

VTV.BeginUpdate;
  VTV.Clear;
  vLevelPrev:=0
  for i := Low(vArray) to High(vArray) do
  begin
    if i > 0 then vLevelPrev:=vArray[i-1].Level;

    Node:=AddNode(VTV,i,vArray[i].Level,vLevelPrev,Node);

    // Create link to your data record into VST node
    Data := VTV.GetNodeData(Node);
    Data.IndexInMyData := i;
    vArray[Data.IndexInMyData].NodePointer := Node;
  end;
  VTV.FullExpand;
  VTV.EndUpdate;

和添加节点:

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode):PVirtualNode;
var j:integer;
begin
   if vI = 0 then
    begin
      Result := vTV.AddChild(nil);
    end
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;

    end;
end;

是否通过在 AddNode 中处理 Data 来减少代码?

解决方案:

我将 Data 作为本地指针放入 AddNode:

function AddNode(vTV: TvirtualStringTree; vI, vLevel, vLevelPrev:integer; vNode:PVirtualNode):PVirtualNode;
var j:integer;
    Data: ^rTreeData;
begin
   if vI = 0 then
      Result := vTV.AddChild(nil)
    else
    begin
      if vLevel = 0 then  Result := vTV.AddChild(nil)
      else if vLevel > vLevelPrev then  Result := vTV.AddChild(vNode)
      else if vLevel < vLevelPrev then
      begin
        Result := vNode.Parent;
        for j := 1 to (vLevelPrev - vLevel) do
          Result := Result.Parent;
        Result := vTV.AddChild(Result);
      end
      else
      begin
        Result := vNode.Parent;
        Result := vTV.AddChild(Result);
      end;
    end;
    Data := VTV.GetNodeData(Result);
    Data.IndexInMyData := vI;
end;

现在我有了使用 AddNode 的最终简化代码:

vLevelPrev := 0;
  for i := Low(vArray) to High(vArray) do
  begin
    if i > 0 then
      vLevelPrev := vArray[i - 1].Level;
    Node := AddNode(VTV, i, vArray[i].Level, vLevelPrev, Node);
    vArray[i].NodePointer := Node;
  end;

【问题讨论】:

  • 我的问题现在是-1,我认为有人否决了它?为什么?当我在这里询问时,我正在努力制作 MCVE。我可以改进什么?
  • 找到从一个代码实例到另一个实例不同的部分,并对其进行参数化。就像任何其他类似的重构一样。你明白这个概念吗?如果是这样,问题是什么?你试过重构失败了吗?
  • @DavidHeffernan 我将工作简化的代码放在编辑中。这是进步吗?
  • 这不是代码审查网站。我不知道你在这里找什么。
  • 试图减少重复的代码,以便每当我使用相同的代码时都可以重复使用它 - 在我的应用程序中大约 150 次,用于不同的数组,不同的虚拟树视图。试图在 AddNode 中放置 Data 变量并对其进行更新,但无法使其工作。把这部分代码Data := VTV.GetNodeData(Node); Data.IndexInMyData := i;放到AddNode中。

标签: delphi delphi-xe7 virtualtreeview


【解决方案1】:

Button2Click 中调用AddNode 时,指针变量Data 仍未初始化并指向一些任意内存,该内存将被写入AddNode 内部,从而导致访问冲突。

我仍然不确定我是否理解,为什么你需要 vData 参数。使vData 成为指向rTreeData 记录的本地指针,就像Button1Click 中的Data 一样,并完全删除该参数。在Button2Click 中使用I 索引到vArray

【讨论】:

  • 我只是想在 AddNode 中放置尽可能多的代码。这是在 AddNode 之外:Data := VTV.GetNodeData(Node); Data.IndexInMyData := i; vArray[Data.IndexInMyData].NodePointer := Node; - 如果我可以将前 2 行放入 AddNode,那就太好了。
  • 我不知道我是否可以删除数据...我使用数据作为返回我的 vArray 的链接 - 比如说我使用的 VTV 的 OnClick:Data := VTV.GetNodeData(VTV.FocusedNode); vArray[Data.IndexInMyData]...
  • 啊哈,刚刚知道了,谢谢!我就是这么做的,效果很好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 2014-01-18
  • 2020-07-02
  • 1970-01-01
  • 2023-03-20
  • 2019-06-07
相关资源
最近更新 更多