To answer the first question, if a class implements IDisposable "properly" then no, there should be no danger of a resource leak. There may however be a delay in which unmanaged resources would remain un-released until garbage collection occurs.
Consider the following application:
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
namespace LazyInit {
class DisposableClass : IDisposable {
private IntPtr _nativeResource = Marshal.AllocHGlobal(100);
private System.IO.MemoryStream _managedResource = new System.IO.MemoryStream();
public string ThreadName { get; set; }
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
~DisposableClass() {
Console.WriteLine("Disposing object created on thread " + this.ThreadName);
Dispose(false);
}
private void Dispose(bool disposing) {
if (disposing) {
// free managed resources
if (_managedResource != null) {
_managedResource.Dispose();
_managedResource = null;
}
}
// free native resources if there are any.
if (_nativeResource != IntPtr.Zero) {
Marshal.FreeHGlobal(_nativeResource);
_nativeResource = IntPtr.Zero;
}
}
}
static class Program {
private static Lazy<DisposableClass> _lazy;
[STAThread]
static void Main() {
List<Thread> t1 = new List<Thread>();
for (int u = 2, i = 0; i <= u; i++)
t1.Add(new Thread(new ThreadStart(InitializeLazyClass)) { Name = i.ToString() });
t1.ForEach(t => t.Start());
t1.ForEach(t => t.Join());
Console.WriteLine("The winning thread was " + _lazy.Value.ThreadName);
Console.WriteLine("Garbage collecting...");
GC.Collect();
Thread.Sleep(2000);
Console.WriteLine("Application exiting...");
}
static void InitializeLazyClass() {
_lazy = new Lazy<DisposableClass>(LazyThreadSafetyMode.PublicationOnly);
_lazy.Value.ThreadName = Thread.CurrentThread.Name;
}
}
}
Using LazyThreadSafetyMode.PublicationOnly
, it creates three threads, each instantiating Lazy<DisposableClass>
then exits.
The output looks something like this:
The winning thread was 1
Garbage collecting...
Disposing object created on thread 2
Disposing object created on thread 0
Application exiting...
Disposing object created on thread 1
As mentioned in the question, Lazy<>.LazyInitValue()
does not check for IDisposable, and Dispose()
is not being called explicitly, yet still all three objects were ultimately disposed; two objects were disposed because they fell out of scope and were garbage collected, and the third was disposed on application exit. This occurs because we're making use of the destructor/finalizer that is called when all managed objects are destroyed, and in turn, using it to ensure that our unmanaged resources are released.
To the second question, it's anyone's guess as to why no IDisposable check was put in place. Perhaps they didn't envision unmanaged resources being allocated upon instantiation.
Further reading:
For more on how to properly implement IDisposable, look on MSDN here (which is where half of my example is sourced from).
Also, there is an excellent SO article here, which gives the best explanation I've ever seen of why IDisposable should be implemented this way.