Lazy instantiation one-liner of instance fields with the coalesce operator

13.05.2008 - 12:00 (2 years, 3 months, 3 weeks ago)

Filed under programming, .NET, TrivadisContent

It is hardly worth blogging, but...

Did you know that the return value of an assignment is the assignment? i.e.

class Person {
  public string Name;
}
...
Person p;
Console.WriteLine((p = new Person()).Name);

And did you know there is a coalesce operator since .NET 2.0 that will return the left-hand side if not null, or the right-hand side if the left-hand is null?

a ?? b;

If you combine these information snippets you get the modern one-liner for lazy instantiation of instance fields:

Person p;
public Person Example {
  get {
    return p ?? (p = new Person());
  }
}

kick it on DotNetKicks.com

Comments

Fredrik ( 14.05.2008 - 09:58 UTC )
Note that this is not thread-safe. Which may be fine, depending on requirements :)
OJ ( 14.05.2008 - 10:14 UTC )
99.9% of lazy instantiation/loading code is not threadsafe :) For every example of threadsafe code you'll fine 100 examples that aren't. That doesn't mean to say that I don't agree with you of course ;) At the end of the day, the coalesce operator is just a bit of syntactic suger, and if you want to write threadsafe code for lazy instantiation you probably wouldn't use it. It's still cool though.
Anonymous ( 14.05.2008 - 11:23 UTC )
Could any of you please provide a description on why this is not thread safe? maybe with an example of a thread safe version.
Fredrik ( 14.05.2008 - 11:36 UTC )
@OJ: Certainly true :) And I agree that the syntax is neat - not many people realise that an assignment returns itself like that. @Anon: It is not threadsafe because there is a chance that between thread A evaluating the condition (p == null) and actually assigning p = new Person(), thread B may come along and evaulate the p == null and find it to be true (because A hasn't finished assigning it yet), resulting in two Person objects being created. To make it thread-safe, you could use the double-checked locking pattern:
private readonly object pLock = new object();
private Person p;

public Person Example
{
    get 
    {
        if(null == p)
        {
            lock(pLock)
            {
                // important because while waiting for the lock, 
                // another thread may have assigned p!
                if(null == p) 
                {
                    p = new Person();
                }
        }

        return p;
    }
}

*) edited by flq for better display of code

Anonymous ( 14.05.2008 - 12:07 UTC )
Thanks. I did not see it that way. Not providing a thread safe solution could potentially create 2 instances of person. Am i right when I say that this problem could be a potential problem, but might not be, as the 2 threads should be just behind each other (one executing the check an the other executing the instantiation). Also, this problem would be hard to find. You will end up with 2 instances of Person, but only use one. A problem in disguise. Just trying to put myself in the position of one, having the problem :)
Heiko Hatzfeld ( 14.05.2008 - 14:35 UTC )
Well... You could imagine a situation where the creation of the object is actually performed on an application server. So it could take "long" to finally get to your process. For examle if you want to fill the object with complex initialisation data from a DB... So making sure its not created multiple times might be a good measure ;)
Thomas ( 15.05.2008 - 09:56 UTC )
Hi Frank, great post. :-) I have alreay known both, but I haven't used the combination... really cool.
Jelle Hissink ( 16.05.2008 - 18:18 UTC )
Why not just combine the two methods?
private readonly object pLock = new object();
private Person p;

public Person Example
{
    get
    {
        if(null == p)
        {
            lock(pLock)
            {
                // important because while waiting for the lock,
                // another thread may have assigned p!
                return p ?? (p = new Person());    
                // or if you prefer replace return with p =
            }
        }

        return p;
    }
}
Quedi ( 16.05.2008 - 19:22 UTC )

Or, for the utterly lazy amongst us:

public Person Example {
  get {
    if (p == null) 
      lock (pLock)
        p = p ?? new Person();
    return p;
  }
}
Anonymous ( 23.06.2008 - 18:46 UTC )
Generally a return from within a lock() is bad form (can cause deadlocks). lock(pLock) { return p ?? (p = new Person()); } Recall the original syntatic sugar of lock() is Monitor.Enter() and Monitor.Exit() Love this example though, very creative.
Jelle Hissink ( 14.07.2008 - 19:58 UTC )
Do you have any references on returns causeing deadlocks? Last time I checked
lock (pLock) {
    return p ?? (p == new Person());
}
equals
System.Threading.Monitor.Enter(pLock);
try {
    return p ?? (p == new Person());
} finally {
    System.Threading.Monitor.Exit(pLock);
}
Since the Exit is inside the finally it will get executed (though I think I might have read some bug reports on .Net 1.0/1.1) But I have not heard any problems with 2.0+

Post a comment