Использование Dispose на синглтоне для очистки ресурсов

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

Прямо сейчас у меня это удаление обрабатывается методом Cleanup() синглтона, который приложение вызывает при закрытии. Когда я писал документацию для Cleanup(), мне пришло в голову, что я описываю, для чего следует использовать метод Dispose(), то есть для очистки ресурсов. Первоначально я не реализовал IDisposable, потому что это казалось неуместным в моем синглтоне, потому что я не хотел, чтобы что-то удаляло сам синглтон. В настоящее время нет, но в будущем может быть причина, по которой этот Cleanup() может быть вызван, но синглтон должен будет существовать. Я думаю, что могу включить GC.SuppressFinalize(this); в метод Dispose, чтобы сделать это возможным.

Поэтому мой вопрос состоит из нескольких частей:

1) Является ли реализация IDisposable на синглтоне принципиально плохой идеей?

2) Я просто смешиваю здесь семантику, имея Cleanup() вместо Dispose(), и, поскольку я распоряжаюсь ресурсами, мне действительно следует использовать dispose?

3) Будет ли реализация 'Dispose()' с GC.SuppressFinalize(this); сделать так, чтобы мой синглтон фактически не уничтожался в случае, если я хочу, чтобы он жил после вызова для очистки базы данных.


person Craig Suchanec    schedule 28.05.2010    source источник


Ответы (3)


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

Теперь поместить его и избавиться от объекта после того, как приложение закончило работу с ним, не обязательно плохо. Вы должны быть осторожны, хотя, когда вы называете это. Если вы действительно заинтересованы в очистке и у вас есть только одна ссылка на нее, вы можете поместить код очистки в финализатор объектов ~YourClass таким образом, что он будет вызываться только тогда, когда .Net уверен, что он больше не нужен (когда приложение закрывается, если это настоящий синглтон).

Я просто смешиваю здесь семантику, используя Cleanup() вместо Dispose(), и, поскольку я избавляюсь от ресурсов, мне действительно следует использовать удаление?

Да, это просто семантика. Dispose — это стандарт, показывающий, что есть вещи, которые необходимо очистить после того, как программа закончит работу с объектом.

Будет ли реализована функция Dispose() с помощью GC.SuppressFinalize(this); сделать так, чтобы мой синглтон на самом деле не уничтожался, если я хочу, чтобы он продолжал жить после вызова для очистки базы данных.

Нет, это означает, что когда вы вызываете метод dispose, сборщик мусора не будет вызывать пользовательский финализатор объекта.

person kemiller2002    schedule 28.05.2010
comment
Будете ли вы вызывать синглтоны .Dispose() в точке выхода приложений? - person LuckyLikey; 26.04.2017
comment
Имейте в виду, что Dispose привязан к оператору using, который вызывает его автоматически. Не верно для пользовательского метода очистки (или любого другого). - person Mike Lowery; 01.06.2020

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

    if (instance == null) {
        var temp = new Singleton();
        if (Interlocked.CompareExchange(ref instance, temp, null) != null) &&
                temp is IDisposable) {
            ((IDisposable)temp).Dispose();
        }
    }
    return instance
    

    Мы создали временный объект и попробовали атомарный Compare-and-Swap, поэтому нам нужно удалить этот временный объект, если он реализует IDisposable и не был записан в местоположение экземпляра.

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

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

    public static Singletong GetInstance() {
      if (instance == null) {
        instance = new Singleton(); //here we re-evalute cache for example
       }
    return instance
    }
    public static void Reset() {
        instance = null;
    } 
    
person Andrey Taptunov    schedule 28.05.2010
comment
Просто хотел указать, что второй пример не является потокобезопасным, и если вам не нужно воссоздавать экземпляр синглтона, лучший способ - инициализировать его, где объявление статической переменной определено, если это возможно. - person haze4real; 14.05.2013

Я согласен с ответом Кевина, но хотел бы кое-что добавить к этому. Я немного смущен вашим утверждением:

Когда приложение закроется, эта база данных должна быть удалена.

Вы действительно имеете в виду удалить? Как в разрушенном? Вы говорите о реальной (SQL) базе данных?

Вы должны понимать, что даже если вы поместите свой код очистки в финализатор или в событие Application_End (ASP.NET), вы не можете гарантировать, что они будут вызваны. Процесс может быть остановлен, или компьютер теряет питание. Кажется более разумным удалить базу данных при запуске приложения или, по крайней мере, иметь запасной механизм при запуске с некоторой очисткой.

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

Я пытаюсь сказать, что когда вы видите эту базу данных как ресурс приложения, вам придется привязывать инициализацию и очистку не к объекту, а к приложению. В приложении ASP.NET это будут Application_Start и Application_End (global.asax). В приложении Windows Forms это будет Program.Main.

Тем не менее, при использовании этих механизмов вместо финализаторов у вас нет уверенности, что ваш код очистки будет выполнен.

person Steven    schedule 28.05.2010
comment
База данных — это встроенная база данных, которую приложение использует для хранения состояния запущенных процессов. Он может вырасти до довольно больших размеров. Если он не удаляется из-за отключения электроэнергии или по какой-либо другой причине, это нормально, потому что пользователь может просто удалить файл из файловой системы. Кроме того, он не всегда удаляется, поскольку пользователь может сохранить его после закрытия приложения. Мой одноэлементный объект отвечает за создание и удаление базы данных (их может быть много в течение жизни приложения), поэтому я уверен, что он должен отвечать за удаление при закрытии. - person Craig Suchanec; 28.05.2010