If Assigned(Foo)
和 If (Foo <> nil)
有什么区别吗?如果有,它们应该在什么情况下使用?TL;DR
官方文档中介绍:
Assigned(P)
对于指针变量,相当于测试P <> nil
;对于过程变量,则相当于测试@P <> nil
。
因此,对于非过程指针变量(例如类型为 PInteger
、PMyRec
、TBitmap
、TList<integer>
或 TFormClass
的变量),Assigned(P)
等同于 P <> nil
。
然而,对于过程变量,Assigned(P)
等同于 @P <> nil
,而 P <> nil
则会尝试执行 P
指向的过程或函数(不带参数)。
Explanation
上述文档摘录已经很好地总结了此问题。 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 <> 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 <> nil
将比较MyPtrFunc
函数的结果值和nil
; 它不会告诉你PF
函数指针是否已分配(它是!)。
第二个PF <> nil
将尝试调用nil
函数指针; 这是一个错误(访问冲突异常)。
要测试过程变量是否已分配,您必须测试@PF <> 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 <> nil
是相同的。
方法
就本话题而言,方法与常规过程的工作方式相同。例如,对于一个方法变量M
,Assigned(M)
等同于@M <> nil
,并且当方法指针不为nil
时为True
。(在底层,我相信@M
产生TMethod
的Code
成员。)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 <> nil
?对于过程型指针,你应该使用 Assigned(X)
还是 @X <> nil
?这完全是个人口味的问题。
个人而言,当我想测试变量是否已经赋值时,我倾向于使用 Assigned(X)
;而当我想测试变量是否未被赋值时,则使用 X = nil
(或者 @X = nil
),仅仅因为not Assigned(X)
不够简洁。
一个相关的警告
当然,无论是 Assigned(X)
还是 X <> nil
(或者 @X <> 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
不会。 - Fabricio AraujoAssigned()可以处理任何对象作为参数,并且它总是你想要调用的函数。 如果你得到一个空值,你绝对想要将其测试为“未赋值”,而不是因为空值<>nil而引发异常。
if foo<>nil
相比,if not Assigned(foo)
有点臃肿。话虽如此,我更喜欢使用Assigned
。 - Ian Boyd