Delphi 中是否有从内部函数访问外部函数结果变量的本机语法?

Is there a native syntax to access the outer function Result variable from inner function in Delphi?

考虑:

function OuterFunc: integer;
  function InnerFunc: integer;
  begin
    // Here I'd like to access the OuterFunc.Result variable
    // for both reading and writing its value
    OuterFunc.Result := OuterFunc.Result + 12;
  end;
begin
end;

是否有本地语法来访问 InnerFunc 中的 OuterFunc Result 变量?或者唯一的方法是将它像参数一样传递,如下所示?

function OuterFunc: integer;
  function InnerFunc(var outerResult: integer): integer;
  begin
  end;
var
  i: integer;
begin
  i := InnerFunc(Result);
end;

你可以通过给函数名赋值来给函数赋值,这实际上是 Pascal 的原始方式:

function MyFunc: integer;
begin
  MyFunc := 2;
  // is equal to the following
  Result := 2;
end;

所以在你的情况下你可以写

function OuterFunc: integer;
  function InnerFunc: integer; 
  begin
    OuterFunc := 12;
  end;
begin
end;

但是请注意,在语句块中使用函数名称而不是在赋值运算符的左侧会导致递归调用,因此与预定义 Result 的工作方式不同。

换句话说,您无法从 InnerFunc 中访问先前设置的值 OuterFunc。您需要使用例如在 InnerFunc 之前定义的外部作用域中的局部变量也可以从 InnerFunc:

访问
function OuterFunc: integer;
var
  OuterResult: integer;

  function InnerFunc: integer; 
  begin
    OuterResult := 0;
    OuterResult := OuterResult + 12;
  end;
begin
  Result := OuterResult;
end;

有关详细信息,请参阅 the documentation 中的 Function Declarations

除了使用本机 Pascal 语法(如 Tom Brunberg 的回答所示)之外,另一种选择是将本地函数转换为过程。

function OuterFunc: integer;
  procedure InnerFunc(out innerResult: integer); 
  begin
    {OuterFunc's} Result := 0;
    innerReuslt := -1;
  end;
var
  i: integer;
begin
  InnerFunc( i );
end;

由于这是您的 INNER 本地函数,您不会通过这个简单的更改破坏一些外部 API/contract。

两次,因为您的原始代码 InnerFunc 是事实上的过程,调用者和被调用者都没有使用它自己的 Result

function OuterFunc: integer;
//  function InnerFunc: integer;
  procedure InnerFunc; 
  begin
    // here i'd like to access OuterFunc.Result variable
    // for both reading and writing its value
//    OuterFunc.Result := OuterFunc.Result + 12;
    Result := Result + 12;
  end;
begin
  InnerFunc();
end; 

但是好吧,假设您只是忘记使用两个函数的两个结果,但您原本打算这样做。 您仍然没有多少方法可以偷工减料并破解 Delphi 语言意图限制。

从该过程方法开始,您可以添加一个函数 shorthand,如果您想在表达式中使用这样的函数。 虽然它看起来有点难看,并为 CPU 添加了一个重定向调用(你不能 内联 本地函数,如果你可以 Delphi 内联实现陷入困境 "register dances"),因此会稍微减慢速度(但取决于您对它的称呼 w.r.t。其他工作 - 额外的工作可能不明显)。

function OuterFunc: integer;
  procedure InnerFunc(out innerResult: integer); overload;
  begin
    innerResult := +2;
 //   {OuterFunc's} Result := Result + innerResult;
    Inc( Result, innerResult );
  end;
  function InnerFunc: integer; overload;
  begin
    InnerFunc( Result ); 
  end;
var
  i: integer;
begin
//  InnerFunc( i );
  i := InnerFunc();
end;

还有一个黑客正在声明变量重叠。

function OuterFunc: integer;
var Outer_Result: integer absolute Result;
    i: integer;
  function InnerFunc: integer; 
  begin
    Result := +2;
    Inc( Outer_Result, Result );
  end;
begin
  i := InnerFunc();
end;

现在,这种方法可能会破坏优化,例如将 "result" 放入 CPU 寄存器中,强制为其使用 RAM,这样速度较慢。 此外,一旦您可能希望更改 OuterFunc 的类型,而您忘记相应地更改 Outer_Result var 的类型——您就把自己搞砸了。

function OuterFunc: double; // was - integer; Proved to be not enough since 2020
var Outer_Result: integer absolute Result;  // and here we forgot to sync type changing.... ooooops!
    i: integer;
  function InnerFunc: integer; 
....

表达该意图的更简单的方式(以分配和访问另一个 RAM 变量为代价)是这样的:

function OuterFunc: integer;
{$T+} // we need to enable type checking: predictability is safety
var Outer_Result: ^integer;
    i: integer;
  function InnerFunc: integer; 
  begin
    Result := +2;
    Inc( Outer_Result^, Result );
  end;
begin
  Outer_Result := @Result;
  i := InnerFunc();
end;

但是所有这些选项都是变通,破坏了概念的清晰度,从而阻碍了人们将来 read/understand 该程序的能力。 如果您需要变量 - 那么请声明变量。这将是这里最明确的选择。毕竟,编写程序更多是为了让未来的程序员阅读它们,而不是为了让计算机编译它们。 :-)

function OuterFunc: integer;
var the_Outer_Result: integer;
  function InnerFunc; 
  begin
    Result := +2;
    Inc( the_Outer_Result, Result );
  end;
var
  i: integer;
begin
  the_Outer_Result := 0;

  .....
    I := InnerFunc();
  .....

  Result := the_Outer_Result;
end;

这样你就不会与语言抗争,而是放弃并按原样使用它。与语言作斗争和超越语言总是很有趣,但从长远来看,当你必须维护任何人最后一次阅读 5 年前的代码并将其移植到 Delphi/libraries/Windows 的更新版本时——那么这种非自然的聪明技巧往往变得很烦人。