Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>This is a very broad question with many different angles.</p> <p><strong>The meaning of the <code>Assigned</code> function</strong></p> <p>Much of the code in your question betrays an incorrect understanding of the <code>Assigned</code> function. The <a href="http://docwiki.embarcadero.com/VCL/en/System.Assigned" rel="noreferrer">documentation</a> states this:</p> <blockquote> <p>Tests for a nil (unassigned) pointer or procedural variable.</p> <p>Use Assigned to determine whether the pointer or procedure referenced by P is nil. P must be a variable reference of a pointer or procedural type. Assigned(P) corresponds to the test P&lt;> nil for a pointer variable, and @P &lt;> nil for a procedural variable. </p> <p>Assigned returns False if P is nil, True otherwise.</p> <p><strong>Note</strong>: Assigned cannot detect a dangling pointer--that is, one that is not nil but no longer points to valid data. For example, in the code example for Assigned, Assigned does not detect that P is not valid.</p> </blockquote> <p>The key points to take from this are:</p> <ol> <li><code>Assigned</code> is equivalent to testing <code>&lt;&gt; nil</code>.</li> <li><code>Assigned</code> cannot detect whether the pointer or object reference is valid or not.</li> </ol> <p>What this means in the context of this question is that </p> <pre><code>if obj&lt;&gt;nil </code></pre> <p>and</p> <pre><code>if Assigned(obj) </code></pre> <p>are completely interchangeable.</p> <p><strong>Testing <code>Assigned</code> before calling <code>Free</code></strong></p> <p>The implementation of <code>TObject.Free</code> is very special.</p> <pre><code>procedure TObject.Free; begin if Self &lt;&gt; nil then Destroy; end; </code></pre> <p>This allows you to call <code>Free</code> on an object reference that is <code>nil</code> and doing so has no effect. For what it is worth, I am aware of no other place in the RTL/VCL where such a trick is used.</p> <p>The reason why you would want to allow <code>Free</code> to be called on a <code>nil</code> object reference stems from the way constructors and destructors operate in Delphi. </p> <p>When an exception is raised in a constructor, the destructor is called. This is done in order to deallocate any resources that were allocated in that part of the constructor that succeeded. If <code>Free</code> was not implemented as it is then destructors would have to look like this:</p> <pre><code>if obj1 &lt;&gt; nil then obj1.Free; if obj2 &lt;&gt; nil then obj2.Free; if obj3 &lt;&gt; nil then obj3.Free; .... </code></pre> <p>The next piece of the jigsaw is that <a href="http://docwiki.embarcadero.com/RADStudio/en/Methods#Constructors" rel="noreferrer">Delphi constructors initialise the instance memory to zero</a>. This means that any unassigned object reference fields are <code>nil</code>. </p> <p>Put this all together and the destructor code now becomes</p> <pre><code>obj1.Free; obj2.Free; obj3.Free; .... </code></pre> <p>You should choose the latter option because it is much more readable.</p> <p>There is one scenario where you need to test if the reference is assigned in a destructor. If you need to call any method on the object before destroying it then clearly you must guard against the possibility of it being <code>nil</code>. So this code would run the risk of an AV if it appeared in a destructor:</p> <pre><code>FSettings.Save; FSettings.Free; </code></pre> <p>Instead you write</p> <pre><code>if Assigned(FSettings) then begin FSettings.Save; FSettings.Free; end; </code></pre> <p><strong>Testing <code>Assigned</code> outside a destructor</strong></p> <p>You also talk about writing defensive code outside a destructor. For example:</p> <pre><code>constructor TMyObject.Create; begin inherited; FSettings := TSettings.Create; end; destructor TMyObject.Destroy; begin FSettings.Free; inherited; end; procedure TMyObject.Update; begin if Assigned(FSettings) then FSettings.Update; end; </code></pre> <p>In this situation there is again no need to test <code>Assigned</code> in <code>TMyObject.Update</code>. The reason being that you simply cannot call <code>TMyObject.Update</code> unless the constructor of <code>TMyObject</code> succeeded. And if the constructor of <code>TMyObject</code> succeeded then you know for sure that <code>FSettings</code> was assigned. So again you make your code much less readable and harder to maintain by putting in spurious calls to <code>Assigned</code>.</p> <p>There is a scenario where you need to write <code>if Assigned</code> and that is where the existence of the object in question is optional. For example</p> <pre><code>constructor TMyObject.Create(UseLogging: Boolean); begin inherited Create; if UseLogging then FLogger := TLogger.Create; end; destructor TMyObject.Destroy; begin FLogger.Free; inherited; end; procedure TMyObject.FlushLog; begin if Assigned(FLogger) then FLogger.Flush; end; </code></pre> <p>In this scenario the class supports two modes of operation, with and without logging. The decision is taken at construction time and any methods which refer to the logging object must test for its existence.</p> <p>This not uncommon form of code makes it even more important that you don't use spurious calls to <code>Assigned</code> for non-optional objects. When you see <code>if Assigned(FLogger)</code> in code that should be a clear indication to you that the class can operate normally with <code>FLogger</code> not in existence. If you spray gratuitous calls to <code>Assigned</code> around your code then you lose the ability to tell at a glance whether or not an object should always exist. </p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload