все еще пытаясь понять схему утилизации - PullRequest
2 голосов
/ 03 августа 2010

Я прочитал MSDN и различные посты о шаблоне утилизации, но есть пара вещей, которые я не понимаю.Я написал следующий код для проверки шаблона утилизации.Обратите внимание, что нет неуправляемых ресурсов, я использую vs2008 и .net 3.5:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void tryDispose()
    {
        //test 1 : allocate resource and leave manage it to gc
        BL.myclass df = new BL.myclass();
        //test 2 : try to force garbage collecting
        //GC.Collect();
       //test 3 : call dispose myself
       //using (BL.myclass df = new BL.myclass())
        //{ 

        //}

    }

    private void button1_Click(object sender, EventArgs e)
    {
        tryDispose();
    }

это мой одноразовый класс:

class myclass: IDisposable
{

    private StronglyTypedDs myDS;
    private bool _disposed;

    public myclass()
    {
        using (myDSTableAdapter docDocadpt = new myDSTableAdapter())
        {
            myDS = new StronglyTypedDs();
            docDocadpt.Fill(myDS.TheTable);
        }
    }


    #region IDisposable Members

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

    ~myclass()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                if (myDS != null)
                    myDS .Dispose();
                myDS = null;
            }
        }
        _disposed = true;
    }


    #endregion

}

Результаты:

test 1a - просто создавая экземпляр myclass, деструктор комментируется, так как myclass не содержит неуправляемых ресурсов: myclass.dispose не вызывается, даже если я закрываю приложение (вместо которого выполняется dispose).Каково состояние набора данных, когда я закрываю приложение?

test 1b - раскомментируйте деструктор, все в порядке, все утилиты вызываются, когда я закрываю приложение.

test 2a и 2b - я выполняю вышеуказанный тест, просто вызывая gc.collect: поведение идентичночтобы проверить 1a и 1b соответственно

тест 3 - все работает нормально (конечно)

во многих сообщениях говорится, что если myclass не содержит неуправляемые ресурсы, мне не нужно добавлятьдеструктор;Тем не менее, в моих тестах, если я не добавлю деструктор, myclass.dispose не будет вызван, когда я закрою приложение.Я не ждал, пока gc запустится сам (если я правильно помню, gc.collect не гарантирует освобождение экземпляра класса), чтобы проверить, вызовет ли он myclass.dispose.Итак, какова правильная реализация: всегда определять деструктор или избегать его, если myclass содержит только управляемые ресурсы?Если бы я заполнил все уровни поколений, мог бы gc с именем myclass избавиться или нет, не реализовав деструктор?

Наконец, я заметил, что если я определяю деструктор, но не объявляю класс как реализующий IDisposable, цепочка удаления работает в любом случае.Это может иметь смысл, поскольку деструктор может быть переведен для финализации в IL.Но я нахожу это действительно запутанным: это какая-то «неявная» реализация интерфейса, которую я не знаю?gc может распоряжаться предметом, но пользователи не могут

заранее поблагодарить

Стефано

Ответы [ 6 ]

10 голосов
/ 03 августа 2010

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

Обычно это означает, что вы должны реализовывать IDisposable только тогда, когда вы:

  1. Оборачиваем родной ресурс.(В этом случае вам, вероятно, также нужен финализатор.)
  2. Инкапсулирует класс, который реализует IDisposable.(В этом случае вы хотите IDisposable, но вам не нужно реализовывать финализатор / деструктор.)

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

1 голос
/ 04 августа 2010

Я написал краткую серию под названием Как реализовать IDisposable и финализаторы: 3 простых правила .В нем описан гораздо более простой подход, которым Microsoft придерживалась после версии BCL 2.0.

«Официальный» шаблон излишне сложен и бесполезен.

1 голос
/ 03 августа 2010

Ваш код верен, вы реализовали его точно , как это задокументировано в библиотеке MSDN.

Вам, однако, придется взглянуть еще раз.Причина, что происходит, когда работает деструктор (он же финализатор).Аргумент располагающий будет ложным, защищенный метод Dispose ничего не делает .Это совершенно нормально, финализаторы должны только высвобождать неуправляемые ресурсы.У тебя их нет.Исключительно редко когда-либо иметь неуправляемый ресурс в своем собственном коде.Они входят в классы-обертки, доступные в .NET, чтобы превратить неуправляемый рабочий ресурс в хороший управляемый класс.Если вы думаете, что вам нужен финализатор, вы ошибетесь в 99,99% случаев.Даже если вы оберните неуправляемый ресурс, вам следует использовать один из упаковщиков SafeHandle.И положитесь на их финализаторы.

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

public void Dispose()
{
    if (myDS != null) myDS.Dispose();
}

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

Далее вы неправильно понимаете, как вызывается метод Dispose ().Это не автоматически.В этом и заключается смысл того, чтобы автоматически освобождать ресурсы уже позаботился сборщик мусора.Метод Dispose () предназначен для , который вы должны вызывать, либо с помощью оператора , используя , либо вызывая его напрямую.Так что вы можете освободить ресурс раньше, а не ждать, пока поток финализатора сборщика мусора дойдет до него.Что может занять некоторое время.Позвоните ему, когда вы узнаете , что ваша программа больше не будет использовать объект.

Если ваш DataSet активно используется формой, вы не сможете утилизировать его, пока форма не закроется.Вызвать метод класса Dispose () в обработчике событий FormClosed.Или, лучше, откройте файл формы Designer.cs, вырезайте и вставляйте метод Dispose (), который вы там найдете, и перемещайте его в файл исходного кода формы.И добавьте вызов утилизации.Я знаю, что это немного сбивает с толку, но единственный раз, когда можно редактировать файл конструктора.

1 голос
/ 03 августа 2010

Ладно, я понял, что, ссылаясь на мой пример, правильно реализовать реализацию, поскольку набор данных является глобальным для моего класса и реализует IDisposable, в то время как мне не нужен финализатор, потому что нет неуправляемых ресурсов.Даже если я "забуду" избавиться от некоторого управляемого ресурса в моем методе dispose класса, gc соберет его в какой-то момент.Метод dispose - это просто утилита, которую я предоставляю другим классам / разработчикам для управляемых ресурсов, обязательна для финализатора, если я заверну неуправляемые ресурсы.

Я прочитаю статьи, которые вы предоставили как можно скорее, но в целомраз у меня последний вопрос: когда gc освободит память, принадлежащую моему классу и его ресурсам?когда кто-то вызывает dispose или когда он запустится (он освободит память вместо того, чтобы перенести ее в следующее поколение)?

спасибо всем за ваши ответы и примеры

1 голос
/ 03 августа 2010

Правильный шаблон - использовать финализатор, только когда ваш класс содержит неуправляемые ресурсы. Что касается использования GC для утилизации ваших управляемых объектов, не делайте этого. Контракт IDisposable разъясняет, что этот объект необходимо утилизировать.

0 голосов
/ 03 августа 2010

Основная цель IDisposable состоит в том, чтобы иметь согласованный стандартный интерфейс, с помощью которого можно распоряжаться неуправляемыми ресурсами, то есть, если вы не сделали что-то, чтобы вызывать Dispose (), эти ресурсы зависли бы после закрытия приложения. Также понятно, что синтаксис using (), который использует, реализует для вас следующий блок:

DisposableType someDisposable = new DisposableType();
try
{
    // Do whatever
}
finally
{
    ((IDisposable)someDisposable).Dispose();
}

Все это реализовано в хорошем дизайне, например:

using(DisposableType someDisposable = new DisposableType())
{
    // Do whatever
}
...