我需要澄清这个案子。
根据我的测试,Result变量定义为: 第一行中的Boolean = False,Integer = 0,String ='',Object = nil等。 但我从未见过这方面的官方参考。 这也是有意义的,因为这给出了提示。
[DCC警告] Unit1.pas(35):H2077分配给'TForm1.Test'的值从未使用过
function TForm1.Test: Boolean;
begin
Result := False;
// Some arbitrary code here
Result := True;
end;
但是如果我注释掉第一行并且在最后一行之前的某个地方有异常会发生什么?结果=假吗?
如果Result未定义,这意味着我总是必须通过在以后异常的情况下定义Result来启动每个函数。这对我来说毫无意义。
答案 0 :(得分:15)
正如official Delphi documentation所述,结果是:
一般规则是默认情况下不定义结果值。你必须设置它。编译器会警告您任何缺少的结果集。
对于字符串,动态数组,方法指针或变体结果, 效果与函数结果声明为一样 声明参数后面的附加var参数。其他 单词,调用者传递另一个指向a的32位指针 返回函数结果的变量。
准确地说,var
参数不仅适用于托管类型,而且仅适用于record
或object
结果,这些结果在调用之前在堆栈上分配,因此受制于同样的行为。
也就是说,例如,如果您的结果是string
,则会将其作为额外的var
参数传递。因此它默认包含调用前的值。它首先是''
,然后如果你多次调用该函数,它将包含以前的值。
function GetString: string;
// is compiled as procedure GetString(var result: string);
begin
if result='' then
result := 'test' else
writeln('result=',result);
end;
function GetRaise: string;
// is compiled as procedure GetRaise(var result: string);
begin
result := 'toto';
raise Exception.Create('Problem');
end;
var s: string;
begin
// here s=''
s := GetString; // called as GetString(s);
// here s='test'
s := GetString; // called as GetString(s);
// will write 'result=test' on the console
try
s := GetRaise; // called as GetRaise(s);
finally
// here s='toto'
end;
end;
所以我的建议是:
var
参数传递的,而不是out
参数; exception
都会照常处理 ,也就是说,正在运行的流程会跳转到下一个finally
或except
块 - 但如果你有结果作为var
参数传输,而某些内容已分配给result
,则会设置该值; exit()
语法在较新版本的Delphi上返回值。答案 1 :(得分:10)
你说:
如果Result未定义,这意味着我总是必须通过在以后异常的情况下定义Result来启动每个函数。
如果函数引发异常,则担心函数的返回值是未定义的。但那应该不重要。请考虑以下代码:
x := fn();
如果函数fn
的主体引发异常,则返回呼叫站点,不应分配x
。从逻辑上讲,上面的单线可以被认为是一个双线:
fn()
x
如果在第1行引发异常,则第2行永远不会发生,并且永远不应将x
分配给。
因此,如果在分配给Result
之前引发异常,那么这根本不是问题,因为如果函数引发异常,则永远不应该使用函数的返回值。
你应该关注的是一个相关的问题。如果您分配到Result
和然后会引发异常怎么办?您分配给Result
的值是否可以在函数外传播?可悲的是答案是肯定的。
对于许多结果类型(例如,整数,布尔等),如果该函数引发异常,则分配给Result
的值不会传播到函数外部。到目前为止,非常好。
但对于某些结果类型(字符串,动态数组,接口引用,变体等),有一个实现细节会使问题复杂化。返回值作为var
参数传递给函数。事实证明,您可以从函数外部初始化返回值。像这样:
s := 'my string';
s := fn();
当fn
的正文开始执行时,Result
的值为'my string'
。好像fn
声明如下:
procedure fn(var Result: string);
这意味着您可以分配到Result
变量并查看呼叫站点的修改,即使您的函数随后引发异常也是如此。没有干净的方法来解决它。您可以做的最好的事情是分配函数中的局部变量,并仅将Result指定为函数的最终行为。
function fn: string;
var
s: string;
begin
s := ...
... blah blah, maybe raise exception
Result := s;
end;
此处缺乏C风格return
声明。
很难准确地说明哪种类型的结果变量容易受到上述问题的影响。最初我认为问题只影响了托管类型。但Arnaud在评论中指出记录和对象也会受到影响。好吧,如果记录或对象是堆栈分配的,那就是这样。如果它是全局变量或堆分配(例如类的成员),则编译器会以不同方式对待它。对于堆分配的记录,使用隐式堆栈分配变量来返回函数结果。仅当函数返回时才将其复制到堆分配变量。因此,在调用站点分配函数结果变量的值会影响函数本身的语义!
在我看来,这是一个非常明确的例子,说明为什么在语言设计中,函数返回值具有var
语义而不是out
语义是一个可怕的错误。< / p>
答案 2 :(得分:5)
不,Result
没有(保证)默认值。除非你给它一个值,否则它是未定义的。 the documentation暗示了这一点,其中陈述
如果函数退出而没有为Result或者赋值 函数名,然后函数的返回值是未定义的。
我刚试过
function test: integer;
begin
ShowMessage(IntToStr(result));
end;
并收到包含文字35531136
的消息。