LINQ is great, isn’t it? I freakin’ love it. Along with LINQ, came our also-now-familiar friend, IEnumerable<T>. Whenever I’m passing around a collection now, I pass around IEnumerable. It’s flexible and can always be changed in to whatever I need at the time (List<T>, Array, etc) as well as supports all the awesome LINQ stuff that we all know and love.
Y’know what else I love? Lazy<T>. Talk about speeding up your load times. If you’re not using it, you should be.
But here is where it gets tricky. What if I have a collection of Lazy objects? Use case would be something like in my app I have all the cards in a deck. I don’t need/want to spin through and create a representation of every single card at start up, let’s just fetch them as they’re needed, and cache the resulting object (a perfect job for Lazy<T>).
The deck would, naturally, be an IEnumerable<T>, right? Sweet. So let’s create a faux unit test to somewhat show what I’m talking about:
WTF? I created my collection of Lazies, looped through each one and forced creation of the value, but the IsValueCreated came back false? That means the next time I ask for any item in the collection, or every item, they’ll all be created AGAIN? That’s not what Lazy does!
But it is what LINQ does.
LINQ, if you recall is a deferred execution paradigm. When you define a LINQ expression, you are defining a query to be run when it’s needed – not immediately. So in my example above, the _myObjects variable is a query, not an actual collection. When I enumerate (via foreach) _myObjects, the query is run again. Which, as you can see, re-creates the collection of Lazy<MyObject> instances. That’s why the 3rd loop (line 33) fails.
So we still want all the benefits of Lazy, but we’d also like to be able to use LINQ to create, filter, sort, etc any of our Lazy objects… what to do… The solution is force execution of the LINQ query when you create the collection. You can do this any number of ways, but my favorite looks something like this:
Notice the new ‘.ToArray()’ call on line 10. By running .ToArray(), the LINQ query has to run NOW so that I can get an Array back. This effectively creates an actual array of actual Lazy<T>’s whose states will be maintained.
And it works (no console output calling out problems)!
LINQ is awesome and Lazy<T> is handy as heck, but using them together requires some thought. Be sure to comment your code when you’re working with this or invariably you’ll have somebody lop off the .ToArray() there, and kill all your performance gain and state.