首页 > 解决方案 > 在方法链中用作输出和(非参考)输入的值

问题描述

考虑以下方法链接的最小示例,其中浮点变量out由早期方法设置(使用参数),然后(使用const参数)传递给链中的后续方法:

program ChainedConundrum;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  ValueType = Double;
  TRec = record
    function GetValue(out AOutput: ValueType): TRec;
    procedure ShowValue(const AInput: ValueType);
  end;

function TRec.GetValue(out AOutput: ValueType): TRec;
begin
  AOutput := 394;
  Result := Self;
end;

procedure TRec.ShowValue(const AInput: ValueType);
begin
  Writeln(AInput);
end;

var
  R: TRec;
  Value: ValueType = 713;

begin
  R.GetValue(Value).ShowValue(Value);
  Readln;
end.

我最初希望这会打印浮点数394(以某种格式),但它没有(必然);当我使用 Delphi 10.3.2 的 32 位编译器构建程序时,程序会打印713. 使用调试器单步执行程序确认初始GetValueValue被传递给ShowValue

但是,如果我使用 64 位编译器构建它,394则会打印出来。同样,如果我ValueType从更改DoubleInt32,我会同时获得394两个版本。Int64产生39464 位和71332 位。字符串产生更新的值。类就像记录一样工作。然而,与实例方法相反,类方法总是给我更新的值。当然,放弃方法链 ( R.GetValue(Value); R.ShowValue(Value)) 也是如此。

毫不奇怪,如果我将AInput参数ShowValueconst(或未修饰的值)参数更改为var参数,我总是会得到更新的值。

我的结论是

  1. 不允许在这样的方法链中同时设置和传递变量,或者
  2. 编译器中有一个错误。

我的问题是:它是什么?如果不允许,文档在哪里说明了这一点?到目前为止,我还没有找到相关的段落。(短语“序列点”似乎很少出现在 WWW 上的短语“Delphi”附近。)

标签: delphiparametersundefined-behaviormethod-chaining

解决方案


在这里或其他地方对此问题发表评论的每个人都同意这“感觉像”或“显然”是编译器错误。

我在 Embarcadero Jira 创建了问题RSP-29733

转向可能的解决方法,请注意问题似乎是编译器使用了变量的旧值。因此,当值更改接近变量的使用时,就会出现问题。

但是,变量的地址没有改变,所以如果你通过引用而不是值传递变量,问题就消失了。一种方法是在第二次传递值时使用var参数,即使您不需要,甚至在语义上也不需要。

因此,一种更自然的方法似乎是使用const [Ref]参数:

procedure ShowValue(const [Ref] AInput: ValueType);

这与未修饰的参数具有相同的语义,const但强制编译器通过引用传递变量,从而避免了错误。


推荐阅读