Подтвердить что ты не робот

Почему Dispose() должен быть не виртуальным?

Я новичок в С#, поэтому извиняюсь, если это очевидный вопрос.

В примере MSDE Dispose, метод Dispose, который они определяют, не является виртуальным. Почему это? Мне кажется странным - я ожидал бы, что дочерний класс IDisposable, у которого есть свои не управляемые ресурсы, просто переопределит Dispose и вызовет base.Dispose() в нижней части своего собственного метода.

Спасибо!

4b9b3361

Ответ 1

Типичное использование заключается в том, что Dispose() перегружен, с открытым, не виртуальным методом Dispose() и виртуальным, защищенным Dispose (bool). Открытый метод Dispose() вызывает Dispose (true), а подклассы могут использовать этот защищенный виртуальный метод, чтобы освободить свои собственные резорбции и вызвать base.Dispose(true) для родительских классов.

Если класс, владеющий общедоступным методом Dispose(), также реализует финализатор, тогда финализатор вызывает Dispose (false), указывая, что во время сбора мусора был вызван метод protected Dispose (bool).

Если есть финализатор, тогда публичный метод Dispose() также отвечает за вызов GC.SuppressFinalize(), чтобы убедиться, что финализатор больше не активен и никогда не будет вызван. Это позволяет сборщику мусора нормально относиться к классу. Классы с активными финализаторами обычно собираются только в крайнем случае, после очистки gen0, gen1 и gen2.

Ответ 2

Это, конечно, не очевидно. Этот шаблон был особенно выбран, потому что он хорошо работает в следующем сценарии:

  • Классы, у которых нет финализатора.
  • Классы, имеющие финализатор.
  • Классы, которые могут быть наследованы.

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

Ответ 3

Хотя методы в интерфейсе не являются "виртуальными" в обычном смысле, они тем не менее могут быть реализованы в классах, наследующих их. Это, по-видимому, удобство, встроенное в язык С#, позволяющее создавать методы интерфейса, не требуя ключевого слова virtual, и методы реализации, не требуя ключевого слова override.

Следовательно, хотя интерфейс IDisposable содержит метод Dispose(), перед ним нет ключевого слова virtual, и вам не нужно использовать ключевое слово override в классе наследования для его реализации.

Обычный шаблон Dispose предназначен для реализации Dispose в вашем собственном классе, а затем вызывает Dispose в базовом классе, чтобы он мог освободить ресурсы, которыми он владеет, и так далее.

Тип Dispose должен выпустить все ресурсы, которыми он владеет. Это должен также освободить все ресурсы принадлежащих его базовым типам, путем родительский метод Dispose. Тип родительского типа Dispose освободить все ресурсы, которыми он владеет, и в свою очередь вызывают его родительский тип Dispose метод, распространяющий этот шаблон через иерархию базовых типов.

http://msdn.microsoft.com/en-us/library/fs2xkftw.aspx

Ответ 4

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

В коде шаблона ожидается, что не виртуальная функция Dispose всегда будет одинаковой в родительском и дочернем [просто вызывает Dispose (True)], поэтому никогда не нужно переопределять ее. Вся работа выполняется в виртуальном Dispose (Boolean).

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

С другой стороны, для некоторых видов методов могут быть преимущества наличия метода не виртуального базового класса, чье задание состоит в цепочке к защищенному виртуальному методу и называть виртуальный метод Dispose(bool) на самом деле нет хуже, чем VirtDispose(), даже если поставленный аргумент бесполезен. В некоторых ситуациях, например, может потребоваться, чтобы все операции над объектом охранялись блокировкой, принадлежащей объекту базового класса. Если не виртуальный базовый класс Dispose получит блокировку перед вызовом виртуального метода, он освободит все базовые классы от необходимости беспокоиться о самой блокировке.

Ответ 5

Метод Dispose не должен быть виртуальным, поскольку он не является точкой расширения для шаблона для реализации одноразового использования. Это означает, что базовый одноразовый класс в иерархии создаст политику верхнего уровня (алгоритм) для размещения и передаст детали другому методу (Dispose(bool)). Эта политика верхнего уровня является стабильной и не должна быть переопределена дочерними классами. Если вы разрешаете дочерним классам переопределять ее, они могут не вызывать все необходимые фрагменты алгоритма, которые могут оставить объект в несогласованное состояние.

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

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

Ответ 6

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

Ответ 7

У меня есть подробное объяснение шаблона dispose здесь. По сути, вы предоставляете метод protected для переопределения, который более устойчив для неуправляемых ресурсов.

Ответ 8

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