34

Is there any difference between If Assigned(Foo) and If (Foo <> nil)? If So, when should they each be used?

2
  • 7
    Assigned() reads better Commented Dec 19, 2010 at 22:24
  • 1
    @DavidHeffernan: But if not Assigned(foo) get's kind of bloated compared to if foo<>nil. Having said that, i prefer Assigned Commented Oct 13, 2011 at 19:59

2 Answers 2

49

TL;DR

The official documentation states

Assigned(P) corresponds to the test P <> nil for a pointer variable, and @P <> nil for a procedural variable.

Hence, for a non-procedural pointer variable (such as a variable of type PInteger, PMyRec, TBitmap, TList<integer>, or TFormClass), Assigned(P) is the same thing as P <> nil.

However, for a procedural variable, Assigned(P) is the same thing as @P <> nil, while P <> nil would try to execute the procedure or function that P points to (with an empty list of parameters).

Explanation

The documentation excerpt above summarizes it pretty well. Assigned(X) returns True if and only if the X variable, which has to be a pointer under the hood (with some exception), has a non-nil value.

Assigned can be used for "old-school" pointer variables:

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 can also be used for object (and metaclass) variables. Indeed, in Delphi, an object (or metaclass) variable is simply a pointer under the hood:

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

(And, for completeness, an example with a metaclass variable:

var FC: TFormClass; begin FC := TForm; { Assigned(FC) True } { FC <> nil True } FC := nil; { Assigned(FC) False } { FC <> nil False } 

)

In all these examples, Assigned(X) is exactly the same thing as X <> nil.

However, for procedural types, things are slightly different.

First, let's warm up:

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; 

Notice in particular that SP is used to actually invoke the procedure it currently points to.

Now, you might try

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; 

but that will not even compile. The compiler says, "Not enough actual parameters". The reason is that the above code will try to execute the procedure that SP points to, and then indeed the required AText parameter is missing. (Of course, at compile time, the compiler doesn't know if SP will point to a compatible procedure or not, but it does know the signature of such a valid procedure.)

And even if the procedural type had an empty parameter list, it wouldn't compile, since a procedure doesn't return a value (much less a value that can be compared against nil).

But beware! The following code will compile:

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; 

The first PF <> nil will compare the MyPtrFunc function result value against nil; it will not tell you whether the PF function pointer is assigned or not (it is!).

The second PF <> nil will try to invoke a nil function pointer; that's a bug (access violation exception).

To test if a procedural variable is assigned, you have to test @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; 

For procedural variables, Assigned(X) is the same thing as @X <> nil, as stated by the documentation.

Methods

Methods work as regular procedures as far as this topic is concerned. For instance, for a method variable M, Assigned(M) is equivalent to @M <> nil and is True iff the method pointer is not nil. (Under the hood, I believe @M yields the Code member of TMethod.)

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; 

What to use?

So, should you use Assigned(X) or X <> nil for non-procedural pointers? And should you use Assigned(X) or @X <> nil for procedural pointers? It is entirely a matter of taste.

Personally, I tend to use Assigned(X) when I want to test if the variable is assigned, and X = nil (or @X = nil) when I want to test if the variable is not assigned, simply because not Assigned(X) is less compact.

A related warning

Of course, both Assigned(X) and X <> nil (or @X <> nil) only test if the pointer is nil or not; if non-nil, the pointer might still point to garbage. For instance, since local non-managed variables are not initialized in Delphi, they might well be non-nil before they are assigned a value, but in that case they point to garbage:

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). } 

Another example:

 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! } 
Sign up to request clarification or add additional context in comments.

2 Comments

I think that the question is just about language. In the same fashion we have MyType(Myvar).Prop which is the same as (MyVar as MyType).Prop
@az01: if MyVar does not have an instance of MyType as fail with an exception. MyType(MyVar).Prop doesn't.
-1

Assigned() can handle any object as arguments and is always what you want to call. If you get a null value, you definitly want to test it as "not assigned" rather than have an exception because null <> nil.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.