【问题标题】:Assigned vs <> nil赋值 vs <> nil
【发布时间】:2010-12-19 16:55:12
【问题描述】:

If Assigned(Foo)If (Foo &lt;&gt; nil) 有什么区别吗?如果是,应该分别在什么时候使用?

【问题讨论】:

  • Assigned() 读起来更好
  • @DavidHeffernan:但与if foo&lt;&gt;nil 相比,if not Assigned(foo) get 有点臃肿。话虽如此,我更喜欢Assigned

标签: delphi pointers null


【解决方案1】:

TL;DR

The official documentation 状态

Assigned(P) 对应于测试 P &lt;&gt; nil 用于指针变量,@P &lt;&gt; nil 用于过程变量。

因此,对于非过程指针变量(如PIntegerPMyRecTBitmapTList&lt;integer&gt;TFormClass 类型的变量),Assigned(P) 与@ 相同987654331@.

但是,对于过程变量,Assigned(P)@P &lt;&gt; nil 相同,而 P &lt;&gt; nil 会尝试执行P 指向的过程或函数(带有参数的空列表)。

说明

上面的文档摘录很好地总结了它。 Assigned(X) 返回 True 当且仅当 X 变量必须是底层指针(有一些例外),具有非 nil 值。

Assigned 可用于“老式”指针变量:

var
  i: Integer;
  p: PInteger;
begin
  i := 5;

  p := @i;
  { Assigned(p)        True }
  { p <> nil           True }

  p := nil;
  { Assigned(p)        False }
  { p <> nil           False }

Assigned 也可用于对象(和元类)变量。事实上,在 Delphi 中,对象(或元类)变量只是底层的指针:

L := TList<integer>.Create;
try
  { Assigned(L)        True }
  { L <> nil           True }
finally
  FreeAndNil(L);
end;

{ Assigned(L)          False }
{ L <> nil             False }

(并且,为了完整起见,一个带有元类变量的示例:

var
  FC: TFormClass;
begin

  FC := TForm;
  { Assigned(FC)       True }
  { FC <> nil          True }

  FC := nil;
  { Assigned(FC)       False }
  { FC <> nil          False }

)

在所有这些示例中,Assigned(X)X &lt;&gt; nil 完全相同。

但是,对于程序类型,情况略有不同。

首先,让我们热身:

type
  TStringProc = procedure(const AText: string);

procedure MyStrProc(const AText: string);
begin
  ShowMessage(AText);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  SP: TStringProc;
begin
  SP := MyStrProc;
  SP('test');
end;

特别注意SP 用于实际调用它当前指向的过程。

现在,你可以试试

procedure TForm1.FormCreate(Sender: TObject);
var
  SP: TStringProc;
begin
  SP := MyStrProc;
  ShowMessage(BoolToStr(Assigned(SP), True)); { True }
  ShowMessage(BoolToStr(SP <> nil, True)); { will not compile }

  SP := nil;
  ShowMessage(BoolToStr(Assigned(SP), True)); { False }
  ShowMessage(BoolToStr(SP <> nil, True)); { will not compile }
end;

但这甚至不会编译。编译器说,“没有足够的实际参数”。原因是上面的代码会尝试执行SP指向的过程,然后确实缺少所需的AText参数。 (当然,在编译时,编译器不知道SP 是否会指向一个兼容的过程,但它确实知道这样一个有效过程的签名。)

即使过程类型有一个空的参数列表,它也不会编译,因为过程不返回值(更不用说可以与nil 比较的值了)。

但要小心!以下代码编译:

type
  TGetPtrFunc = function: pointer;

function MyPtrFunc: pointer;
begin
  Result := nil;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  PF: TGetPtrFunc;
begin
  PF := MyPtrFunc;
  ShowMessage(BoolToStr(Assigned(PF), True)); { True }
  ShowMessage(BoolToStr(PF <> nil, True)); { False (!) }

  PF := nil;
  ShowMessage(BoolToStr(Assigned(PF), True)); { False }
  ShowMessage(BoolToStr(PF <> nil, True)); { will cause access violation at runtime }
end;

第一个PF &lt;&gt; nil 会将MyPtrFunc 函数结果值与nil 进行比较;它不会告诉您是否分配了 PF 函数指针(它是!)。

第二个PF &lt;&gt; nil 将尝试调用nil 函数指针;这是一个错误(访问冲突异常)。

要测试是否分配了过程变量,您必须测试@PF &lt;&gt; nil

procedure TForm1.FormCreate(Sender: TObject);
var
  SP: TStringProc;
begin
  SP := MyStrProc;
  ShowMessage(BoolToStr(Assigned(SP), True)); { True }
  ShowMessage(BoolToStr(@SP <> nil, True)); { True }

  SP := nil;
  ShowMessage(BoolToStr(Assigned(SP), True)); { False }
  ShowMessage(BoolToStr(@SP <> nil, True)); { False }
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  PF: TGetPtrFunc;
begin
  PF := MyPtrFunc;
  ShowMessage(BoolToStr(Assigned(PF), True)); { True }
  ShowMessage(BoolToStr(@PF <> nil, True)); { True }

  PF := nil;
  ShowMessage(BoolToStr(Assigned(PF), True)); { False }
  ShowMessage(BoolToStr(@PF <> nil, True)); { False }
end;

对于过程变量,Assigned(X)@X &lt;&gt; nil 相同,如文档所述。

方法

就本主题而言,方法与常规程序一样工作。例如,对于方法变量MAssigned(M) 等价于@M &lt;&gt; nil,并且当方法指针不是nil 时为True。 (在幕后,我相信@M 会产生TMethodCode 成员。)

procedure TForm1.FormCreate(Sender: TObject);
var
  M: TNotifyEvent;
begin
  M := Self.FormClick;
  ShowMessage(BoolToStr(Assigned(M), True)); { True }
  ShowMessage(BoolToStr(@M <> nil, True)); { True }

  M := nil;
  ShowMessage(BoolToStr(Assigned(M), True)); { False }
  ShowMessage(BoolToStr(@M <> nil, True)); { False }
end;

使用什么?

那么,对于非过程指针,您应该使用Assigned(X) 还是X &lt;&gt; nil?您应该使用Assigned(X)@X &lt;&gt; nil 作为程序指针吗?这完全是一个品味问题。

个人,当我想测试变量是否被赋值时,我倾向于使用Assigned(X),而当我想测试变量是否为未分配,只是因为not Assigned(X) 不那么紧凑。

相关警告

当然,Assigned(X)X &lt;&gt; nil(或@X &lt;&gt; nil)都只测试指针是否为nil;如果不是nil,则指针可能仍指向垃圾。例如,由于局部非托管变量在 Delphi 中没有初始化,它们在被赋值之前很可能是非nil,但在这种情况下它们指向垃圾:

procedure TForm1.FormCreate(Sender: TObject);
var
  L: TList<integer>; { local non-managed variable: not initialized }
begin
  Assigned(L) { True or False (chance). If True, it points to garbage data. }
              { Bad things will happen if you try to use L as a list here }
              { (especially if L is not nil). }

另一个例子:

  L := TList<integer>.Create;
  try
    { Do things with L }
  finally
    L.Free;
  end;

  Assigned(L); { True, but L points to garbage -- don't use it as a list! }

【讨论】:

  • 我认为问题只是关于语言。以同样的方式,我们有 MyType(Myvar).Prop 与 (MyVar as MyType).Prop 相同
  • @az01:如果 MyVar 没有 MyType as 的实例,则失败并出现异常。 MyType(MyVar).Prop 没有。
【解决方案2】:

Assigned() 可以将任何对象作为参数处理,并且始终是您想要调用的对象。 如果你得到一个空值,你肯定想将它测试为“未分配”而不是有一个异常,因为 null nil.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-20
    • 1970-01-01
    • 1970-01-01
    • 2020-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多