Синглтон с финализатором, но не 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 живым, даже если бы не было других ссылок на него.