Подтвердить что ты не робот

Где вызывать Dispose() IDisposable, созданного в конструкторе?

Где вызывать Dispose() для IDisposable объектов, принадлежащих объекту?

public class MyClass
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log = "MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }


    private readonly EventLog log;
    private readonly FileStream stream;

    // Other members, using the fields above
}

Должен ли я использовать Finalize() для этого примера? Что делать, если я вообще ничего не реализую? Будут ли проблемы?

Моя первая мысль заключалась в том, что MyClass должен реализовывать IDisposable. Но следующее утверждение в статье MSDN смутило меня:

Реализовать IDisposable только при непосредственном использовании неуправляемых ресурсов. Если ваше приложение просто использует объект, который реализует IDisposable, не предоставляют реализацию IDisposable.

Является ли это утверждение неправильным?

4b9b3361

Ответ 1

Если MyClass принадлежит ресурс IDisposable, тогда MyClass должен быть IDisposable, и он должен распоряжаться инкапсулированным ресурсом, когда Dispose() вызывается в MyClass:

public class MyClass : IDisposable {
    // ...
    public virtual void Dispose() {
        if(stream != null) {
            stream.Dispose();
            stream = null;
        }
        if(log != null) {
            log.Dispose();
            log = null;
        }
    }
}

Нет, вы не должны реализовывать финализатор.

Примечание: альтернативная реализация может быть примерно такой:

private static void Dispose<T>(ref T obj) where T : class, IDisposable {
    if(obj != null) {
        try { obj.Dispose(); } catch {}
        obj = null;
    }
}

public virtual void Dispose() {
    Dispose(ref stream);
    Dispose(ref log);
}

Ответ 2

Для объектов, содержащих другие объекты IDisposable, это хорошая и рекомендуемая практика для реализации IDisposable на вашем собственном объекте, поэтому другие потребители ваш тип может обернуть его в оператор using:

public class MyClass : IDisposable
{
    public MyClass()
    {
        log = new EventLog { Source = "MyLogSource", Log="MyLog" };
        FileStream stream = File.Open("MyFile.txt", FileMode.OpenOrCreate);
    }


    private readonly EventLog log;
    private readonly FileStream stream;

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // Free managed objects here
            stream.Dispose();
        }
    }

    // Other members, using the fields above
}

В вашем случае вы не освобождаете какие-либо управляемые ресурсы, поэтому финализатор не нужен. Если бы вы были, вы бы выполнили финализатор и вызывали Dispose(false), указывая вашему методу dispose, что он работает из потока финализатора.

Если вы не реализуете IDisposable, вы оставляете его в GC для очистки ресурсов (например, закройте Handle на FileStream, который вы открыли), когда он запускает коллекция. Допустим, ваш объект MyClass имеет право на сбор и в настоящее время находится в генерации 1. Вы оставите свой дескриптор FileStream открытым до тех пор, пока GC не очистит ресурс после его запуска. Кроме того, во многих реализациях Dispose вызывается GC.SuppressFinalize, чтобы не подвергать объект другому циклу GC, переходя от очереди инициализации к F- Достижимая очередь.

Ответ 3

Большая часть советов, окружающих Dispose и Finalize, была написана людьми, которые ожидали, что Finalize будет работоспособным в качестве механизма очистки первичного ресурса. Опыт показал, что такое ожидание было чрезмерно оптимистичным. Объекты с открытым доступом, которые приобретают любые ресурсы и удерживают их между вызовами методов, должны реализовывать IDisposable и не должны переопределять Finalize. Если объект содержит любые ресурсы, которые в противном случае не были бы очищены, если они были оставлены, он должен инкапсулировать каждый такой ресурс в частный объект, который затем должен использовать Finalize для очистки этого ресурса, если это необходимо.

Обратите внимание, что класс, как правило, не должен использовать Finalize для очистки ресурсов, хранящихся другими объектами. Если Finalize работает на объекте, который содержит ссылку на другой объект, обычно применяется одно из нескольких условий:

  • Никакая другая ссылка не существует для другого объекта, и она уже выполнила Finalize, поэтому этому объекту не нужно ничего делать, чтобы очистить его.
  • Никакая другая ссылка не существует для другого объекта, и она еще не запущена Finalize, но планируется сделать это, поэтому этому объекту не нужно ничего делать, чтобы очистить его.
  • Что-то еще все еще использует другой объект, поэтому этот объект не должен пытаться его очистить.
  • Другой метод очистки объекта нельзя безопасно запускать из контекста потока финализатора, поэтому этот объект не должен пытаться его очистить.
  • Этот объект стал доступен только для запуска Finalize, потому что вся необходимая очистка уже выполнена, поэтому этому объекту не нужно ничего делать, чтобы очистить вещи.

Определите только метод Finalize в тех случаях, когда можно понять, почему ни одно из вышеперечисленных условий не применяется. Хотя такие случаи существуют, они редки, и лучше не иметь метод Finalize, чем иметь неулокальный.