Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The trouble with Delphi is that you always inherit the <code>Create</code> constructor from <code>TObject</code>. But we can deal with that pretty nicely! Here's a way:</p> <pre><code>TTrueSingleton = class private class var FSingle: TTrueSingleton; constructor MakeSingleton; public constructor Create;reintroduce;deprecated 'Don''t use this!'; class function Single: TTrueSingleton; end; </code></pre> <p>As you can see we can have a private constructor and we can hide the inherited <code>TObject.Create</code> constructor! In the implementation of <code>TTrueSingleton.Create</code> you can raise an error (run-time block) and the <code>deprecated</code> keyword has the added benefit of providing compile-time error handling!</p> <p>Here's the implementation part:</p> <pre><code>constructor TTrueSingleton.Create; begin raise Exception.Create('Don''t call me directly!'); end; constructor TTrueSingleton.MakeSingleton; begin end; class function TTrueSingleton.Single: TTrueSingleton; begin if not Assigned(FSingle) then FSingle := TTrueSingleton.MakeSingleton; Result := FSingle; end; </code></pre> <p>If at compile time the compiler sees you doing this:</p> <pre><code>var X: TTrueSingleton := TTrueSingleton.Create; </code></pre> <p>it will give you the <code>deprecated</code> warning complete with the provided error message. If you're stubborn enough to ignore it, at run time, you'll not get an object but a raised exception.</p> <hr> <p><strong>Later edit</strong> to introduce thread-safety. First of all I must confess, for my own code I don't care about this kind of thread-safety. The probability for two threads accessing my singleton creator routine within such a short time frame it causes two <code>TTrueSingleton</code> objects to be created is so small it's simply not worth the few lines of code required.</p> <p>But this answer wouldn't be complete without thread safety, so here's my take on the issue. I'll use a simple <em>spin-lock</em> (busy waiting) because it's efficient when no locking needs to be done; Besides, it only locks <em>ones</em></p> <p>For this to work an other class var needs to be added: <code>class var FLock: Integer</code>. The Singleton class function should look like this:</p> <pre><code>class function TTrueSingleton.Single: TTrueSingleton; var Tmp: TTrueSingleton; begin MemoryBarrier; // Make sure all CPU caches are in sync if not Assigned(FSingle) then begin Assert(NativeUInt(@FLock) mod 4 = 0, 'FLock needs to be alligned to 32 bits.'); // Busy-wait lock: Not a big problem for a singleton implementation repeat until InterlockedCompareExchange(FLock, 1, 0) = 0; // if FLock=0 then FLock:=1; try if not Assigned(FSingle) then begin Tmp := TTrueSingleton.MakeSingleton; MemoryBarrier; // Second barrier, make sure all CPU caches are in sync. FSingle := Tmp; // Make sure the object is fully created when we assign it to FSingle. end; finally FLock := 0; // Release lock end; end; Result := FSingle; end; </code></pre>
 

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