Знание этого вопроса было задано 4 года назад, но я не довольствуюсь ответами. Я добавляю ответ, который сочетает в себе некоторые моменты, обсуждаемые в ответах и комментариях, с дополнительными аспектами.
Доработка:
Как указала @jrista, давайте проясним, что IDisposable не имеет ничего общего с GC или финализацией как таковой - это всего лишь соглашение и настоятельно рекомендуемая практика. Используя шаблон Dispose , вы можете вызвать метод Dispose из финализатора (как указано @Stephen Cleary). В этом случае вы абсолютно не должны вызывать какие-либо события, , и при этом он не должен обращаться к другим управляемым объектам .
Оставляя в стороне проблемы Dispose / Finalizer, поскольку вашим классам не нужен Finalizer, поскольку они не содержат неуправляемые ресурсы, однако существуют и другие проблемы.
Утечки памяти / Соответствие времени жизни:
Это часто упоминаемая проблема с событиями, которая также может относиться к вашей реализации транзакции. Если у вас есть издатель событий, срок жизни которого превышает срок действия подписчика на событие, вы можете столкнуться с утечкой памяти, если этот подписчик не откажется от подписки на событие, потому что издатель продолжит удерживать его. Если ваши транзакции являются довольно долгоживущими, и вы подписываете на них много недолговечных объектов, вам следует подумать о реализации dispose в этих объектах, а затем отписаться от транзакции. См. Должен ли я всегда отключать обработчики событий в методе Dispose?
Принцип наименьшего сюрприза:
Это хорошая идея, чтобы «злоупотреблять» распоряжаться для совершения транзакции? Я бы сказал нет, хотя есть прецеденты. Возьмите Stream
для примера. Обычно Stream.Dispose
реализуется для вызова Flush
и, таким образом, передает данные на базовый носитель. Однако обратите внимание, что у нас есть явный метод Flush
, поэтому вы должны добавить его. Я считаю, что «избавление от коммита» нарушает принцип наименьшего удивления, явный метод Commit
намного яснее (вы все равно можете вызывать его с Dispose
, если вы хотите использовать это поведение по умолчанию).
Каскады событий / недопустимые состояния объектов: Я думаю, что это самый сильный аргумент в пользу того, чтобы не вызывать события в Dispose
. События имеют тенденцию к каскадированию (то есть одно событие вызывает другие события и код), и если вы не будете осторожны, вы можете оказаться в ситуации, когда часть кода решит, что было бы хорошей идеей перезвонить объекту, который удаляется и, таким образом, может быть в недопустимом состоянии. Нет удовольствия отлаживать, особенно если к объекту могут обращаться несколько потоков! Хотя, опять же, для этого есть прецеденты, такие как Component.Disposed .
Я бы посоветовал не вызывать события из метода Dispose
. Когда вы заканчиваете время существования издателя событий, действительно ли имеет значение, что все его подписчики соответственно обновляют свое состояние? В большинстве случаев я обнаруживаю, что в любом случае я избавляюсь от всего графа объектов (то есть издатель живет дольше, чем подписчики). В некоторых ситуациях вы также можете активно подавлять любые состояния сбоя, возникающие во время утилизации (например, при закрытии соединения TCP).