应该在调用函数之前初始化变量吗?

Mik*_*nni 11 delphi variable-initialization delphi-xe7

当我调用函数来获取值时,我通常初始化varible,以防函数失败或不返回任何内容,我想避免处理未初始化的变量.我对字符串,整数或任何其他类型都这样做.

整数变量的示例:

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');

IF vPropValue > 0 Then
...
Run Code Online (Sandbox Code Playgroud)

这是我最常用的方式.

我知道我可以使用:

If GetPropValue(vObject,'Height') > 0 Then
...
Run Code Online (Sandbox Code Playgroud)

但是在第一个例子中,我避免多次调用函数,如果我稍后需要在代码中再次得到结果.

对于字符串也是一样的(即使我知道本地字符串被初始化为空字符串,而整数不是可以保存任何值)

vName := '';
vName := GetObjectName(vObject,'ObjectName');

IF Trim(vPropStrValue) <> '' Then
...
Run Code Online (Sandbox Code Playgroud)

我知道我可以采取措施避免重复的值赋值,比如确保函数在所有内容都失败时返回0.但是我有100多个函数而且我不能依赖我从来没有弄错过函数如何处理所有事情而且我确定有些人不会返回0,如果一切都失败了.

我试图理解为什么这不是理想的做法以及如何最好地避免它.

编辑

以下是函数未返回正确值或0的示例:

function GetValue(vType:integer):integer;
begin
   if vType=1 then
    Result:=100
   else if (vType>2) and (vType<=9) then
     Result:=200;

end;

procedure TForm1.Button1Click(Sender: TObject);
var vValue:integer;
begin

  vValue:=GetValue(11);
  Button1.Caption:=IntToStr(vValue);

end;
Run Code Online (Sandbox Code Playgroud)

在这种情况下,函数返回的值是一些随机数.

在这种情况下,初始化似乎是有效的方法.或不?

编辑2:

正如大卫在他的回答中指出的那样,正确的是,有一个警告

[dcc32 Warning] Unit1.pas(33): W1035 Return value of function 'GetValue' might be undefined
Run Code Online (Sandbox Code Playgroud)

但是,我无视它,没有理由,只是没看到那里.因为它让我编译它,我认为它没关系.所以,我确实寻找警告并且我"修复"了很多具有类似问题的函数,因为所有IF结果可能都没有被定义.

编辑3和结论:

我希望它增加了问题和解释的范围:

也许我在大多数函数中使用的另一个扭曲的例子,也可以解释为什么我认为我的变量初始化是需要的,我不确定我的函数会一直正常运行,特别是在嵌套函数的情况下.很多人仍然这样设置:

function GetProperty(vType:integer):integer;
begin
  Try
    if vType = 99 then
      Result:=GetDifferentProperty(vType)// <-- Call to another Function, that could return whatever...
    else
    begin
       if vType=1 then
          Result:=100
       else if (vType>2) and (vType<=9) then
         Result:=200;
    end;
  except
  end;
end;
Run Code Online (Sandbox Code Playgroud)

现在我正在解决这些问题,Try Except End;但是有些功能已经有10年了,并且根据我当时的经验,期望它们100%工作,这是不可依赖的.

作为这个项目中唯一的开发人员,我认为我应该相信我的函数(以及他的其余代码),但我无法想象在多个开发人员环境中所有函数都已正确设置.

所以我的结论是:由于我没有处理基础知识 - 设计合理的函数,我需要进行所有这些检查(变量初始化,Try Except行...)以及其他一些不必要的东西.

Dav*_*nan 15

假设这vPropValue是一个局部变量,那么这个代码

vPropValue := 0;
vPropValue := GetPropValue(vObject,'Height');
Run Code Online (Sandbox Code Playgroud)

是无法区分的

vPropValue := GetPropValue(vObject,'Height');
Run Code Online (Sandbox Code Playgroud)

一个更简单的例子可能是这样的:

i := 0;
i := 1;
Run Code Online (Sandbox Code Playgroud)

什么是分配的角度0i,然后立即分配1i?所以,你肯定永远不会写那个.你会写:

i := 1;
Run Code Online (Sandbox Code Playgroud)

在您的代码中,在此答案的顶部,您为同一个变量分配两次.第一个分配中分配的值立即替换为第二个分配中指定的值.因此,第一项任务毫无意义,应予以删除.

第二个例子有点复杂.假设您的函数编写正确,并始终分配给它们的返回值,那么这vName是一个局部变量

vName := '';
vName := GetObjectName(vObject,'ObjectName');
Run Code Online (Sandbox Code Playgroud)

是无法区分的

vName := GetObjectName(vObject,'ObjectName');
Run Code Online (Sandbox Code Playgroud)

我添加额外附加条件的原因与函数返回值的实现的怪癖有关,如下所述.这种情况与上述情况之间的区别在于返回值类型.这是一个托管类型,string而在第一个例子中,类型是一个简单的类型Integer.

同样,考虑到函数总是分配给返回值的条件,第一个赋值是没有意义的,因为值立即被替换.删除第一个作业.


关于编辑中的函数,如果启用提示和警告,编译器将警告您错误的实现.编译器会告诉您并非所有代码路径都返回一个值.

function GetValue(vType:integer):integer;
begin
  if vType=1 then
    Result:=100
  else if (vType>2) and (vType<=9) then
    Result:=200;
end;
Run Code Online (Sandbox Code Playgroud)

如果两个条件都不满足,则不会为结果变量赋值.这个功能应该是:

function GetValue(vType:integer):integer;
begin
  if vType=1 then
    Result:=100
  else if (vType>2) and (vType<=9) then
    Result:=200
  else
    Result:=0;
end;
Run Code Online (Sandbox Code Playgroud)

我不能强调你总是从函数返回一个值是多么重要.事实上,Delphi甚至允许编译你的函数是一个可怕的弱点.


您的双重赋值有时对您有用的原因是由于Delphi中函数返回值的实现的怪癖.与几乎所有其他语言不同,某些更复杂类型的Delphi函数返回值实际上是一个var参数.所以这个功能

function foo: string;
Run Code Online (Sandbox Code Playgroud)

实际上,在语义上,与此相同:

procedure foo(var result: string);
Run Code Online (Sandbox Code Playgroud)

这是德尔福设计师做出的一个非常奇怪的决定.在大多数其他语言中,如C,C++,C#,Java等,函数返回值类似于从被调用者传递给调用者的按值参数.

这意味着,如果您希望有悖常理,可以通过返回值将值传递给函数.例如,考虑以下代码:

// Note: this code is an example of very bad practice, do not write code like this

function foo: string;
begin
  Writeln(Result);
end;

procedure main;
var
  s: string;
begin
  s := 'bar';
  s := foo;
end;
Run Code Online (Sandbox Code Playgroud)

当你打电话时main,它会输出bar.这是一个相当奇怪的实现细节.你不应该依赖它.让我重复一遍.你不应该依赖它.不要养成在呼叫站点初始化返回值的习惯.这导致无法维护的代码.

而是遵循简单的规则,确保函数返回值始终由函数指定,并且在分配之前永远不会读取.


关于函数返回值的实现的更多细节由文档提供,我强调:

以下约定用于返回函数结果值.

  • 如果可能,在CPU寄存器中返回序数结果.在AL中返回字节,在AX中返回单词,在EAX中返回双字.
  • 在浮点协处理器的栈顶寄存器(ST(0))中返回实际结果.对于Currency类型的函数结果,ST(0)中的值缩放10000.例如,在ST(0)中将Currency值1.234作为12340返回.
  • 对于字符串,动态数组,方法指针或变体结果,效果与在声明的参数之后将函数结果声明为附加var参数的效果相同 .换句话说,调用者传递一个额外的32位指针,该指针指向一个返回函数结果的变量.
  • 在EDX:EAX中返回Int64.
  • 在EAX中返回指针,类,类引用和过程指针结果.
  • 对于静态数组,记录和设置结果,如果该值占用一个字节,则在AL中返回; 如果该值占用两个字节,则在AX中返回; 如果值占用四个字节,则在EAX中返回.否则,结果将在 声明的参数之后传递给函数的其他var参数中返回.

  • @Arioch'我试图强调的是,虚拟不是为调用"函数作为一个过程"的特殊情况创建的,而是它始终存在. (2认同)