在Delphi XE和XE4中,对象如何初始化

6

我有一个类

type
  TLoadOption = class
  private
    FAutoSearch: Boolean;
  public
    property AutoSearch: Boolean read FAutoSearch write FAutoSearch;
  end;

在其中一个函数中,我正在堆栈中创建该类的对象。
procedure MyView.InitializeForm(const aMsg: MyMsg);
//---------------------------------------------------------------------------
var
  Options: TLoadOption;
begin

  if aMsg.OptionalObject <> nil then
   Options := aMsg.OptionalObject as TLoadOption;

  if Assigned(Options) and Options.AutoSearch then
    DoRefresh;
end;

我在aMsg中没有传递任何内容,理想情况下Options不会被设置。

在Delphi XE中,默认情况下Options设置为nil,所以不会调用DoRefresh函数。但是当我在Delphi XE4中执行相同的代码时,options被初始化为一些随机值,并且AutoSearch始终为true,导致调用了此函数DoRefresh,这是不希望发生的结果。

我想知道是否有任何编译器选项可以将默认值设置为未初始化变量。目前我的唯一解决方案如下:

 procedure MyView.InitializeForm(const aMsg: MyMsg);
    //---------------------------------------------------------------------------
    var
      Options: TLoadOption;
    begin
      Options := nil;

      if aMsg.OptionalObject <> nil then
       Options := aMsg.OptionalObject as TLoadOption;

      if Assigned(Options) and Options.AutoSearch then
        DoRefresh;
    end;

这是一种正确的方法吗?


1
MyMsg是什么?这是一个类、记录还是什么?你提到了默认值,但在你的情况下,这个参数没有默认值。你如何分配默认值? - Darthman
2
编译器没有警告你那段代码吗?它应该告诉你Options未初始化。永远不要忽略编译器的警告。 - Rob Kennedy
1
你接受了提供了最佳解决方案的答案,但实际上并没有回答你所问的问题。你问的是关于本地变量初始化的问题。 - David Heffernan
5个回答

11

7

正如其他答案已经提到的,本地变量未初始化。 然而我想在这里添加的是as是零安全的。这意味着您不需要检查aMsg.OptionalObject是否被分配。您可以只写:

var
  Options: TLoadOption;
begin
  Options := aMsg.OptionalObject as TLoadOption;

  if Assigned(Options) and Options.AutoSearch then
    DoRefresh;
end;

请记住,如果OptionalObject未从TLoadOption继承,则会抛出异常。代码看起来总是这样的。因此,如果您确定可以使用强制转换:

begin
  if Assigned(aMsg.OptionalObject) and TLoadOption(aMsg.OptionalObject).AutoSearch then
    DoRefresh;
end;

6
假设TLoadOption是一个对象(非托管引用类型),您必须自己初始化本地变量。没有选项可以让它们初始化。未初始化的引用类型本地变量(除了像字符串等托管类型)将始终包含堆栈上该位置中存在的任何垃圾数据。这使得使用Assigned不可能。
您可以简化代码,但这已经足够简短了。
var
  Options: TLoadOption;
begin
  if aMsg.OptionalObject <> nil then begin
    Options := aMsg.OptionalObject as TLoadOption;
    if Options.AutoSearch then DoRefresh;
  end;
end;

3

Options是一个本地变量,属于非托管类型。这意味着它没有被初始化。在初始化之前,它可以具有任何值。如果变量有时具有未初始化的nil值,则对您来说只是不幸。

在尝试读取它们之前,您必须初始化本地变量。


谢谢你的回答,但我很好奇为什么它在XE上总是正常工作? - Jeeva
1
@Jeeva他回答说 - 你只是幸运而已。最有可能的解释是调用函数(或某个之前的函数)半可靠地留下了一些更高层次的堆栈(其中您的变量出现),使代码工作 - 现在这已经改变了。 - J...
1
@Jeeva 未初始化的值可以是任何值,包括 nil - David Heffernan
@Jeeva 你确定你使用完全相同的选项进行构建吗?如果更改了一些编译器选项(优化、堆栈帧等),它可能已经发生了变化。 - Stefan Glienke
@DavidHeffernan,您的意思很清楚。我只是加入了一些幽默和文字游戏。Lucky的意思是在强大的机会面前实现预期行为...它不一定意味着“幸运”。 - J...
显示剩余3条评论

1

你根本不需要局部变量。

procedure MyView.InitializeForm(const aMsg: MyMsg);
begin
  if Assigned(aMsg.OptionalObject)
    and (aMsg.OptionalObject is TLoadOption)
    and (aMsg.OptionalObject as TLoadOption).AutoSearch
  then
    DoRefresh;
end;

  1. 你不需要本地变量。
  2. 你不需要对其进行初始化。
  3. 如果OptionalObject没有继承自TLoadOption,什么都不会出错。
  4. 对我来说,这段代码是合乎逻辑的。
- kot-da-vinci
本地变量很好。它使代码更好。它允许您避免一遍又一遍地重复自己。 - David Heffernan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接