【问题标题】:Reference a Delphi variant array without copying引用 Delphi 变体数组而不复制
【发布时间】:2015-01-06 05:34:57
【问题描述】:

这似乎是一个奇怪的请求,但有一个很好的理由(代码生成应用程序)。我将一个变体数组传递给一个过程,该过程包含在一个变体数组中,如下所示:

TVarArray = array of variant;

procedure TMainForm.Button1Click(Sender: TObject);
var
  params: TVarArray;
  numRows: integer;
  numCols: integer;
  i: integer;
  j: integer;
begin
  SetLength(params, 2);
  numRows := 2;
  numCols := 2;

  params[0] := 5;
  params[1] := VarArrayCreate([1, numRows, 1, numCols], varVariant);
  for i := 1 to numRows do
    for j := 1 to numCols do
      params[1][i, j] := i + j;

  TestProc(params);
end;

procedure TMainForm.TestProc(params: TVarArray);
var
  arr: variant;
  p: PVariant;
  v: variant;
begin
  arr := params[1];    // -- Copies the array to arr.
  arr[2, 2] := 99;

  p := @(params[1]);
  p^[2, 2] := 88;      // -- Directly reference the passed-in array.

  v := p^;             // -- Copies the array to v -> How to prevent?
  v[2, 2] := 77;       // -- This should change the value in the original array.

  edit1.Text := VarToStr(arr[2, 2]);             // -- 99
  edit2.Text := VarToStr(params[1][2, 2]);       // -- 88  - should be 77
  edit3.Text := VarToStr(v[2, 2]);               // -- 77
end;

我不想创建数组的副本,所以可以使用 p^[] 直接访问传入的数组。但是,我不想在 TestProc 中使用 p^ 语法,而是更喜欢使用不带 ^ 的变量名。当然,如果我尝试 v := p^ 我只会得到一份副本。有没有办法解决?谢谢!

【问题讨论】:

  • 所以,据我了解,您已经有了解决方案,即使用p^
  • @DavidHeffernan 是的,我可以使用 p^,但是如果他们在生成的代码中看到“^”,则 TestProc 等效项来自我的代码生成器,并且从最终用户的角度来看,他们绑定到被吓到(这是针对非专业编码人员的)。我怀疑这是不可能的,但我想我会问。
  • 你为什么首先使用变体?
  • 通常情况下,生成的代码永远不需要人工查看。它由生成器编写并由编译器读取,仅此而已。鉴于此,生成的代码很少需要看起来漂亮。
  • @RobKennedy 在我的情况下,TestProc 将包含用户代码和生成代码的混合,这一事实更加复杂。

标签: arrays delphi pointers variant


【解决方案1】:

您正在寻找的是一个局部变量,它可以作为其他东西(特别是Variant 数组中的元素)的引用。然而,Delphi 没有提供创建“局部引用”变量的方法。引用仅存在于作为varout 或有时const 传递的参数的上下文中。

也许您可以引入一个子例程并将param[1] 作为var 参数传递。在子例程中,您可以引用该参数,它将为调用者的数组元素起别名。例如:

procedure ModifyVariant(var p: Variant);
begin
  p[2, 2] := 77;
end;

procedure TMainForm.TestProc(params: TVarArray);
var
  p: PVariant;
begin
  p := @params[1];

  ModifyVariant(params[1]);

  Assert(params[1][2, 2] = p^[2, 2]);
end;

ModifyVariant 甚至可以是一个匿名过程,因此您可以在与调用者相同的范围内实现:

procedure TMainForm.TestProc(params: TVarArray);
var
  ModifyVariant: reference to procedure(var x: Variant);
  p: PVariant;
begin
  p := @params[1];

  ModifyVariant := procedure(var v: Variant)
  begin
    v[2, 2] := 77;
  end;

  ModifyVariant(params[1]);

  Assert(params[1][2, 2] = p^[2, 2]);
end;

不过,这些看起来都不是特别吸引人,尤其是如果您担心仅使用指针访问就会“吓到”您代码的使用者。

您提到您希望您的用户将他们自己的代码合并到您生成的代码中。我不建议这样做。毕竟,他们在重新运行您的代码生成器后会做什么?他们肯定会失去他们所做的任何定制。最好将生成的代码分开,最好放在单独的文件中。对于用户自定义,可以提供用户可以实现的回调函数形式的钩子。这样,例如,用户可以提供类似于ModifyVariant 的内容,然后您生成的代码可以简单地调用它。您将拥有“变体引用”,并且生成的代码与用户代码完全分离。

【讨论】:

  • 感谢有趣的建议。正如您所指出的,我已经将生成的代码与用户的代码分开,并在编译时合并它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-06
  • 2018-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
相关资源
最近更新 更多