I’m not sure if you’re still looking into this, but I’ve had to delve into the details of both Lazy<T> and LazyInitializer.EnsureInitialized<T>() recently, so I thought I should share my findings.
First, some numbers. I ran benchmarks using both methods on batches of ten million values using both approaches, testing for memory use with GC.GetTotalMemory(true) and getting Stopwatch timings for instantiation, first value access, and subsequent value accesses:
Lazy<T> Memory Use: 320,000,000 bytes (32B/instance)
EnsureInitialized<T>() Memory Use: N/A
Lazy<T> Instantiation Time: 622.01 ms
EnsureInitialized<T>() Inst. Time: N/A
Lazy<T> First Access: 1,373.50 ms
EnsureInitialized<T>() First Access: 72.94 ms
Lazy<T> Subsequent Accesses: 18.51 ms
EnsureInitialized<T>() Subsequent: 13.75 ms
(I used LazyThreadSafetyMode.PublicationOnly with the Lazy<T>'s, which looks to be the same thread safety approach taken by LazyInitializer by default.)
As you can see, unless I’ve screwed up my tests somehow (never out of the question!), under these circumstances LazyInitializer is superior in just about every quantifiable way. It has no memory or instantiation overhead, and it’s faster both for creating and retrieving the value.
So, why would you want to use Lazy<T>? Well, first, these were the test results on my x64 system, and it’s possible you might get different results under other circumstances.
Lazy<T> can also result in clearer and more concise code. return myLazy.Value; is a lot friendlier than return LazyInitializer.EnsureInitialized(ref myValue, () => GetValue(foo));
Additionally, Lazy<T> makes things a lot simpler if you’re dealing with a value type, or with a reference type that could legitimately be null. With LazyInitializer, you have to use a second boolean field to keep track of whether the value has been initialized, compounding the code clarity issue. Lazy<T> is also simpler to use if you want stricter thread safety.
And in the grand scheme of things, most of the overhead is probably negligible for a lot of applications (although not always — the reason I started looking into this is because I was working on an application involving millions of very small lazily-loaded values, and the 32-byte-per-instance overhead of Lazy<T> was actually starting to become inconvenient).
In the end, unless your application is very memory-intensive, I think it’s usually going to be a matter of personal preference. For non-null reference types, I personally think LazyInitializer.EnsureInitialized<T>() is a more elegant approach, but I can dig the code clarity argument too.