Удаление объектов в деструкторе - PullRequest
5 голосов
/ 01 марта 2010

У меня есть объект, который имеет одноразовый объект в качестве члена.

public class MyClass
{
    private MyDisposableMember member;

    public DoSomething
    {
         using (member = new MyDisposableMember())
         {
             // Blah...
         }
    }
}

В MyClass может быть много методов, каждый из которых требует оператора using. Но что, если я сделал это вместо этого?

public class MyClass
{
    private MyDisposableMember member = new MyDisposableMember();

    public DoSomething
    {
         // Do things with member :)
    }

    ~MyClass()
    {
        member.Dispose();
    }
}

Как видите, member находится в деструкторе. Будет ли это работать? Есть ли проблемы с этим подходом?

Ответы [ 7 ]

10 голосов
/ 01 марта 2010

В идеале, Dispose () должен быть вызван до финализации.Было бы лучше следовать типичному шаблону удаления и позволить пользователю правильно утилизировать () объект, а финализатор должен утилизировать его , если удаление еще не было вызвано .

В этом случае, поскольку вы инкапсулируете IDisposable, вам вообще не нужно реализовывать финализатор.(В момент финализации ваш инкапсулированный член будет завершен, поэтому нет необходимости завершать ваш объект - это просто добавляет накладные расходы.) Для получения дополнительной информации прочитайте статью в блоге, которую я написал по инкапсуляции IDisposable.

7 голосов
/ 01 марта 2010

Вы, вероятно, должны заставить MyClass реализовать IDisposable. Внутри метода Dispose() вызовите member.Dispose();. Таким образом, программист может контролировать, когда член удаляется.

3 голосов
/ 01 марта 2010

НЕ ДЕЛАЙТЕ ЭТОГО!

GC сделает это за вас (косвенно, как объект, который нужно утилизировать, или другой будет содержать деструктор)

MyDisposableMember может даже быть утилизирован GC даже до того, как вы утилизируете его - тогда то, что произойдет, может оказаться не тем, что вы намеревались сделать.

Еще хуже: добавление деструктора (или финализатора) к классу требует дополнительного времени при удалении объекта (гораздо больше времени, поскольку объект останется в памяти по крайней мере для одного цикла сбора и, возможно, даже перейдет в следующее поколение) .

Таким образом, это было бы совершенно бесполезным и даже обратный удар.

1 голос
/ 01 марта 2010

В вашем первом примере элемент на самом деле не является частью состояния объекта, поскольку вы создаете его экземпляр каждый раз, когда он используется, и утилизируете его сразу после этого. Поскольку он не является частью состояния, не моделируйте его как таковой, просто используйте локальную переменную, когда это необходимо.

В более общем смысле вы должны поместить всю логику удаления в Dispose () и реализовать IDisposable, а затем использовать свой класс вместе с использованием или try-finally

0 голосов
/ 26 мая 2018

Когда запускается финализатор, одно из следующего будет верно почти для любого IDisposable объекта, на который он ссылается:

  1. У объекта уже был запущен финализатор, и в этом случае вызов Dispose для объекта будет в лучшем случае бесполезным.

  2. У объекта не будет запущен финализатор, но его финализатор будет запланирован для запуска, поэтому вызов Dispose для объекта будет бесполезным.

  3. Объект по-прежнему будет использоваться чем-то другим, кроме завершаемого объекта, поэтому вызов Dispose для него будет плохим.

Есть несколько ситуаций, когда вызов Dispose в финализаторе может быть полезен, но большинство ситуаций соответствуют случаям, перечисленным выше, которые имеют общую особенность: финализатор не должен вызывать Dispose.

0 голосов
/ 01 марта 2010

Следовать шаблону Microsoft - ваша лучшая ставка, поэтому пользователи вашего класса имеют полный контроль над тем, когда он утилизируется.

public class MyClass : IDisposable
{
    private MyDisposableMember member = new MyDisposableMember();

    public DoSomething
    {
         // Do things with member :)
    }

    ~MyClass()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)  // Release managed resources
        {           
            member.Dispose();
        }

        // Release unmanaged resources
    }
}
0 голосов
/ 01 марта 2010

Единственное, что я вижу неправильно (и это не ошибка), это то, что в операторе using вы явно избавляетесь от объекта в тот момент времени (когда вызывается ваша функция / метод). Деструктор не может быть вызван, они вызываются автоматически. Таким образом, на этом этапе может потребоваться некоторое время для удаления участника. Лучше реализовать интерфейс IDisposeable для MyClass.

...