Чтобы ответить на первый вопрос, если класс реализует IDisposable «правильно», то нет, опасности утечки ресурса быть не должно.Однако может иметь место задержка, при которой неуправляемые ресурсы будут оставаться неосвобожденными до тех пор, пока не произойдет сбор мусора.
Рассмотрим следующее приложение:
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;
}
}
}
Используя LazyThreadSafetyMode.PublicationOnly
, он создает три потока,каждый экземпляр Lazy<DisposableClass>
затем завершается.
Вывод выглядит примерно так:
Поток-победитель был 1
Сборщик мусора ...
Удаление объекта, созданного в потоке 2
Удаление объекта, созданного в потоке 0
Приложение завершается ...
Удаление объекта, созданного в потоке 1
Как уже упоминалось в вопросе, Lazy<>.LazyInitValue()
не проверяет наличие IDisposable, а Dispose()
не вызывается явно, но все же все три объекта были в конечном итоге удалены;два объекта были удалены, поскольку они выпали из области видимости и были собраны мусором, а третий был удален при выходе из приложения.Это происходит потому, что мы используем деструктор / финализатор, который вызывается при уничтожении всех управляемых объектов, и, в свою очередь, используем его для обеспечения освобождения наших неуправляемых ресурсов.
Во втором вопросе этокто-нибудь догадывается, почему не была установлена проверка IDisposable.Возможно, они не предполагали, что неуправляемые ресурсы будут выделяться при создании экземпляра.
Дополнительная информация:
Подробнее о о том, как правильно реализовать IDisposable, см. Здесь MSDN (отсюда и половина моего примера).
Также здесь есть отличная статья SO здесь , которая дает лучшее объяснение, которое я когда-либо видел почему IDisposable должен быть реализован таким образом.