TL;DR
The official documentation 状态
Assigned(P) 对应于测试 P <> nil 用于指针变量,@P <> nil 用于过程变量。
因此,对于非过程指针变量(如PInteger、PMyRec、TBitmap、TList<integer> 或TFormClass 类型的变量),Assigned(P) 与@ 相同987654331@.
但是,对于过程变量,Assigned(P) 与 @P <> nil 相同,而 P <> 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 <> 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),而当我想测试变量是否为未分配,只是因为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! }