Синглтон с финализатором, но не IDisposable
Вот что я понимаю о IDisposable и финализаторах из "CLR через С#", "Эффективный С#" и других ресурсов:
- IDisposable предназначен для дестабилизации очистки управляемых и неуправляемых ресурсов.
- Классы, ответственные за неуправляемые ресурсы (например, дескрипторы файлов), должны реализовать IDisposable и предоставить финализатор, чтобы гарантировать их очистку, даже если код клиента не вызывает Dispose() в экземпляре.
- Классы, отвечающие за управляемые ресурсы, никогда не должны реализовывать финализатор.
- Если у вас есть финализатор, вы должны реализовать IDisposable (это позволяет клиентскому коду поступать правильно и вызывать Dispose(), в то время как финализатор предотвращает утечку ресурсов, если они забывают).
Хотя я понимаю аргументы и согласен со всем вышесказанным, существует один сценарий, в котором, я думаю, имеет смысл нарушить эти правила: одноэлементный класс, ответственный за неуправляемые ресурсы (например, предоставление единой точки доступа к определенным файлам).
Я считаю, что всегда неправильно использовать метод Dispose() для одноэлементного режима, потому что экземпляр singleton должен жить в течение всего срока действия приложения, и если какой-либо клиентский код вызывает Dispose(), тогда вы набиты. Однако вы хотите финализатор, чтобы при выгрузке приложения финализатор мог очистить неуправляемые ресурсы.
Так что наличие одноэлементного класса с финализатором, который не реализует IDisposable, кажется мне разумным делом, но этот тип дизайна противоречит тому, что я понимаю, это лучшие практики.
Это разумный подход? Если нет, то почему нет и каковы превосходные альтернативы?
Ответы
Ответ 1
Если неуправляемый ресурс освобождается только при завершении приложения, вам даже не нужно беспокоиться о финализаторе, так как разгрузка процесса должна иметь дело с этим для вас в любом случае.
Если у вас несколько доменов приложений, и вы хотите иметь дело с доменом приложения, выгрузите одну возможную проблему, но, возможно, вам не нужно заботиться.
Во-вторых, те, кто говорит, что этот проект, возможно, не подходит (и будет затруднять его исправление, впоследствии вы обнаружите, что вам действительно нужны два экземпляра)
Создайте объект (или ленивый объект обертки загрузки) в своей точке входа и передайте его через код туда, где это необходимо, чтобы он дал понять, кто несет ответственность за его предоставление, тогда вы можете изменить решение использовать только один с небольшим воздействием на остальную часть кода (который использует то, что он получает)
Ответ 2
Я бы сказал, что объектно-ориентированные шаблоны проектирования и их последствия не всегда влияют на каждое языковое решение, даже на объектно-ориентированных языках. Конечно, вы можете найти классические шаблоны проектирования, которые проще реализовать на одном языке (Smalltalk), в отличие от другого (С++).
Сказав это, я не уверен, что согласен с предпосылкой, что экземпляр singleton должен быть удален только в конце приложения. Ничего в описаниях шаблонов проектирования, которые я читал для Singleton (или Design Patterns: Элементы многоразового объектно-ориентированного программного обеспечения) упоминают это как свойство этого шаблона. Синглтон должен гарантировать, что только один экземпляр класса существует в любой момент времени; это не означает, что он должен существовать до тех пор, пока существует приложение.
У меня такое ощущение, что на практике многие синглтоны существуют в течение большей части жизни приложения. Однако рассмотрите приложение, использующее TCP-соединение для связи с сервером, но также может существовать в отключенном режиме. При подключении вы хотите, чтобы singleton поддерживал информацию о соединении и состояние соединения. После отсоединения вы можете захотеть сохранить тот же синглтон - или вы можете избавиться от синглтона. Хотя некоторые могут утверждать, что имеет смысл держать синглтон (и я даже могу быть среди них), в самом дизайне нет ничего, что мешает вам избавиться от него - если соединение будет переделано, синглтон может быть создан опять же, поскольку ни один экземпляр этого не существует в этот момент времени.
Другими словами, вы можете создавать сценарии, где логические для одиночных игроков имеют IDisposable.
Ответ 3
Пока ваш финализатор не вызывает методы (например, Dispose) на любых других управляемых объектах, вы должны быть в порядке. Просто помните, что порядок завершения не детерминирован. То есть, если ваш одноэлементный объект Foo содержит ссылку на панель объектов, которая требует удаления, вы не можете надежно написать:
~Foo()
{
Bar.Dispose();
}
Сборщик мусора, возможно, уже собрал Bar.
Если вы рискуете попасть в груду OO goo (т.е. начать войну), одной из альтернатив использованию singleton является использование статического класса.
Ответ 4
В то время как это может привести к ошибкам проверки кода и предупреждениям FxCop, нет ничего по-настоящему неправильного в реализации финализатора без IDisposable. Тем не менее, сделать это на одноэлемент не является надежным способом захвата процесса или срывания AppDomain.
Рискуя предложить субъективный совет по дизайну: если объект действительно без гражданства, сделайте его статическим классом. Если это состояние, вопрос, почему это Singleton: вы создаете изменяемую глобальную переменную. Если вы пытаетесь закрыть приложение, обратитесь к нему, когда ваш основной цикл завершен.
Ответ 5
Применимость Singleton к любой конкретной ситуации в стороне,
Я думаю, что нет ничего плохого в Disposing Singleton. В сочетании с ленивым инстанцированием это просто означает, что вы отпускаете ресурс, если он вам не нужен временно, а затем повторно приобретайте его по мере необходимости.
Ответ 6
Если вы хотите создать синглтон с финализатором, вы, вероятно, должны статически ссылаться на него как на WeakReference. Это потребует немного дополнительной работы для обеспечения безопасности потоков в аксессоре, но это позволит отображать один синглтон, когда никто его не использует (если кто-то впоследствии называет метод GetInstance(), он получит новый экземпляр). Если бы использовалась статическая сильная ссылка, она оставила бы экземпляр singleton живым, даже если бы не было других ссылок на него.