синтаксис утилизации c #

Недавно мне пришлось реализовать функциональность Dispose (), и я наткнулся на однострочный метод, двухстрочный метод и более полные методы.

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

    bool _disposed = false;

    public void Dispose(bool disposing)
    {
        if (!_disposed && disposing)
        {
            _context.Dispose();
        }
        _disposed = true;
    }

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

Является ли этот синтаксис просто, чтобы остановить вызов Dispose () более одного раза?


person dotnetnoob    schedule 26.08.2012    source источник
comment
Вы забыли финализатор. Также сделайте Dispose (bool) виртуальным.   -  person Maarten    schedule 26.08.2012
comment
Это может помочь: Реализация IDisposable и шаблона Dispose Правильно   -  person Şafak Gür    schedule 26.08.2012
comment
Это устаревший шаблон удаления. ИМО, есть мало причин использовать его больше.   -  person CodesInChaos    schedule 26.08.2012
comment
@CodesInChaos - Если это так, что следует использовать вместо него?   -  person dotnetnoob    schedule 26.08.2012
comment
Зависит от ситуации. SafeHandle как прямой владелец неуправляемых ресурсов и простой Dispose метод без финализатора как косвенный владелец.   -  person CodesInChaos    schedule 26.08.2012
comment
@CodesInChaos: Вы не заметили необходимости его использовать в последнее время, это не значит, что он больше не нужен.   -  person Johann Gerell    schedule 26.08.2012
comment
@Maarten Коленный рывок добавление финализатора просто для вызова Dispose (false) обычно не очень хорошо. См. bluebytesoftware.com/blog/2011/11/12/ для получения подробной информации о недостатках финализатора, который не приводит к освобождению неуправляемых ресурсов.   -  person Peter Ritchie    schedule 26.08.2012


Ответы (3)


То, что вы опубликовали, частично является шаблоном удаления < / а>. Как кто-то указал, в финализаторе («деструкторе») должен быть соответствующий Dispose(false). Финализатор следует использовать для удаления неуправляемых ресурсов. Если у вас нет неуправляемых ресурсов, с которыми нужно иметь дело (т.е. вам нечего делать, когда disposing равно false), вам не нужен Dispose(false) и, следовательно, не нужен финализатор. Это означает, что Dispose(true) - единственный путь, поэтому вам не нужен Dispose (bool) (и, следовательно, не нужно реализовывать шаблон Dispose), вы можете переместить его тело в Dispose (и удалить проверку для disposing) и просто реализовать Dispose. Например:

public void Dispose()
{
    _context.Dispose();
}

Классы, которые не реализуют свой собственный финализатор (деструктор), не помещаются в список финализаторов, поэтому нет необходимости вызывать GC.SuppressFinalize.

В общем, этого достаточно, если вы создаете класс. Но иногда вы можете быть производными от классов, реализующих этот шаблон. В этом случае вы должны реализовать его поддержку в своем классе (переопределить Dispose(bool) и выполнить disposing проверку и Dispose любых управляемых ресурсов). Поскольку базовый класс реализует IDisposable и вызывает virtual Dispose(bool) в своем Dispose(), вам не нужно реализовывать Dispose() самостоятельно, но вы должны вызывать Dispose(bool) базы в своем Dispose(bool). Например:

protected override void Dispose(bool disposing)
{
    if(disposing) _context.Dispose();
    base.Dispose(disposing);
}

Если вы вызываете базу и в ней реализован шаблон Dispose, вам также не нужно вызывать GC.SuppressFinalize(), потому что он уже это делает.

Вы можете делать все disposed, если хотите; Я обнаружил, что он скрывает многоцелевые ошибки.

person Peter Ritchie    schedule 26.08.2012
comment
Питер, ты тоже говоришь, что я тоже должен игнорировать GC.SupressFinalize ()? - person dotnetnoob; 26.08.2012
comment
Если у вас нет финализатора, подавлять нечего. Т.е. Классы C # без финализатора (деструктора) не помещаются в список финализации, и их не нужно подавлять. - person Peter Ritchie; 26.08.2012
comment
Питер, спасибо за вашу помощь - все это очень ново, поэтому очень приветствуются ясные объяснения. - person dotnetnoob; 27.08.2012

Это только часть схемы. Другая часть, которой здесь не хватает, - это то, что Dispose(false) будет вызываться финализатором.

Флаг состояния _disposed также можно использовать для проверки и добавления ObjectDisposedExceptions в ваши методы.

Полный шаблон находится здесь

Джон Скит предоставляет полезную информацию здесь, и ИМО этот шаблон излишек для большинства ситуаций, если у вас также нет неуправляемых ресурсов. Если нет, просто избавьтесь от ваших управляемых ресурсов и GC.SuppressFinalize в реализации интерфейса Dispose(). Используйте флаг _disposed, только если вы собираетесь бросить ObjectDisposedExceptions.

person StuartLC    schedule 26.08.2012
comment
Джон на самом деле не вдавался в подробности, за исключением того, что я не часто следую этому шаблону. Джо Даффи подробно описывает финализацию и последствия наличия финализатора, который не освобождает неуправляемые ресурсы, здесь: bluebytesoftware.com/blog/2011/11/12/ - person Peter Ritchie; 26.08.2012

Я использую следующие две формы утилизации в зависимости от потребностей класса:

Метод 1 (для класса с управляемыми и неуправляемыми ресурсами или с производными классами):

class ClassHavingManagedAndUnManagedCode : IDiposable
    {
        private volatile bool _disposed = false;

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    //Do managed disposing here.
                }

                //Do unmanaged disposing here.
            }
        }

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

            _disposed = true;
        }

        ~ClassHavingManagedAndUnManagedCode()
        {
            Dispose(false);
        }
    }

Метод 2 (для класса только с управляемым ресурсом / запечатанного класса / класса, не имеющего дочерних классов):

    class ClassHavingOnlyManagedCode  : IDiposable
    {
        private volatile bool _disposed = false;

        public void Dispose()
        {
            if (!_disposed)
            {
                //Dispose managed objects.
                _disposed = true;
            }
        }
    }

Любые дочерние классы ClassHavingManagedAndUnManagedCode должны просто следовать шаблону защищенного метода удаления и вызывать base.Dispose в конце метода Dispose.

Также защитите все общедоступные методы (по крайней мере, те, которые используют удаленные члены) с помощью метода / проверки, который выдает исключение ObjectDisposedException, если экземпляр класса уже удален.

FxCop всегда будет просить вас реализовать Dispose в форме ClassHavingManagedAndUnManagedCode, даже если у вас нет неуправляемых ресурсов.

person Ganesh R.    schedule 26.08.2012
comment
Если у вас нет неуправляемых ресурсов, финализатор не нужен. Фактически, с его помощью вы увеличили нагрузку на сборщик мусора. Объект не может быть собран до тех пор, пока финализатор не будет запущен, поэтому объект остается активным, пока система не успеет вызвать финализатор. См. В моем ответе подробности о том, что финализатор не нужен и его последствия для Dispose(bool) См. Также bluebytesoftware.com/blog/2011/11/12/ - person Peter Ritchie; 26.08.2012
comment
@PeterRitchie Я согласен с вами, что нам не нужен финализатор, если у нас нет неуправляемого кода. Поэтому я привел два разных варианта шаблона удаления: один для класса с управляемым кодом, а другой - без него. Также, если мы выполняем GC.SupressFinalize, я не уверен, добавляем ли мы какое-либо напряжение в GC. - person Ganesh R.; 26.08.2012
comment
У вас есть ошибка в коде вашего метода 2, вы не избавляетесь от чего-либо, если Dispose не вызывается дважды if(_disposed) {/* dispose managed objects */} _disposed = true; - person Peter Ritchie; 26.08.2012
comment
@PeterRitchie Спасибо. Починил это. - person Ganesh R.; 26.08.2012
comment
Если вы выполняете всю очистку в Dispose (bool), когда disposing истинно и у вас есть финализатор (деструктор), тогда у вас должен быть вызов GC.SuppressFinalize (либо в Dispose () или Dispose (bool), когда disposing is true), чтобы уменьшить нагрузку на GC (ну, финализатор - но без SuppressFinalize вы увеличиваете риск того, что объекты останутся живыми до gen2, что нагружает GC) - person Peter Ritchie; 26.08.2012
comment
@Ganesh - спасибо за вклад, он был очень оценен. Я принял ответ Питера, поскольку он помог мне немного больше понять о Dispose (), но я проголосовал за ваш ответ, поскольку он был особенно полезен и будет использоваться снова в будущем. - person dotnetnoob; 27.08.2012
comment
Существует ли какой-либо сценарий, в котором класс с неуправляемыми ресурсами, требующими очистки на основе Finalize, должен наследовать от класса, цель которого не связана с такой очисткой? В каждом случае, который я могу придумать, было бы лучше обернуть неуправляемый ресурс в его собственный финализируемый объект (который может быть частным классом, вложенным в тип, который его использует), чтобы содержащему классу не нужно было беспокоиться о завершении очистки . - person supercat; 27.08.2012