If you’re anything like me, you’ve looked for ways of making your applications faster. In this article, I’ll show you a somewhat unknown class in the .NET framework that makes lazy creation of objects easy to do and thread safe.
The Old Way
Before we get to the fun stuff, let’s take a look at how you might try to do these things without .NET’s Lazy<T>
classes.
Typically when you design a class, you might start with code like this:
You might eventually find that as the number of things you need to instantiate grows, the performance degrades significantly on creation of the object.
In that case, you might make a decision to lazy load components of the Starship
class. That is, you might make a decision to only instantiate them when they’re needed.
A typical version of that would look like this:
Lots of lines there. Thankfully this can be compressed a bit:
Well, that’s at least concise, though it might not be extremely easy to read.
Both the expanded and the concise version of this do have a threading issue, however.
If two callers are trying to get a property, it’s possible that one might get past the null check and then its thread sleeps while another thread also passes that null check. In this case, two instances of ExpensiveWarpCore
are created and then assigned to _warpCore
. This may be a non-issue, or it may be significant.
In order to fully work around this limitation, you need to do something like the following:
While this is technically safer, you can start to see how doing this several times over in your class as you introduce new lazy-loaded properties would become tedious and make people tend to skim the code.
Skimming is bad and allows bugs to hide, so let’s look for a better way.
Introducing Lazy<T>
.NET provides a generic class called Lazy
which allows you to lazily instantiate objects the first time their value is requested.
Let’s take a look at the same code using Lazy<T>
:
Interestingly, we moved the complexity out of the property getter and put it in the backing field.
Let’s take a look at that lazy initializer:
new Lazy<WarpCore>(() => new ExpensiveWarpCore(), true)
First, we’re invoking the constructor on Lazy
and providing a generic type argument telling the class that it will provide a WarpCore
instance.
Next, we pass in a Func<WarpCore>
argument that is invoked to create the instance we need. In this case, we’re just calling the ExpensiveWarpCore
constructor. Note that if we were just invoking the default constructor on WarpCore
instead of ExpensiveWarpCore
, we wouldn’t even need to specify this Func
value.
Finally, we’re passing in a true
boolean value to the final parameter indicating that this instance should be thread safe.
Closing Thoughts
You can see in just a few lines how you can use Lazy<T>
to safely and concisely instantiate objects exactly when they’re first used, and can do so in a thread safe manner if you choose to.
As with anything, there are tradeoffs to using Lazy. Primarily, these tradeoffs involve syntax which may be hard to read for newer developers.
All in all, Lazy
is something I would strongly recommend adding to your standard practices. The downsides are minimal and the ability to standardize on simple patterns is a big win.
The post Getting Lazy in C# appeared first on Kill All Defects.
Top comments (8)
Lazy<T>
is awesome. Just wanted to mention that it's also built into F# as part of the language:As usual, F# is one step ahead of C#. :)
Love the F# love. Keep preaching it.
You trip me out every time you post cause I think it's me and then I said wait no
Just look for the hair. If you see hair, it's probably you.
I think it's mainly used in back-end projects that interop with C# or stand alone, although I really don't know for sure. You might find F# for fun and profit a good way to get started if you're interested.
Doesn't Lazy initialization violate the "Fail Fast" principle? Isn't it better to know if something is bad as fast as possible?
I don't think that one-time initialization will decrease app performance.
Typically you do this for objects that do some form of initialization and it might be beyond just invoking a constructor. I kept things simple for the purposes of the article, but imagine a repository that fetches data from an external API during initialization.
I don't have a great single resource for this. Check out the fsharp tag on dev.to, would be my recommendation.