Правильная обработка ObjectDisposeException в иерархии классов IDisposable

StackOverflow https://stackoverflow.com/questions/668440

  •  21-08-2019
  •  | 
  •  

Вопрос

При правильной реализации IDisposable большинство реализаций, включая рекомендации по платформе, предлагают включить private bool disposed; член, чтобы безопасно разрешить несколько вызовов Dispose(), Dispose(bool) а также бросить Объектдиспоуздисцептион когда это уместно.

Это отлично работает для одного класса.Однако, когда вы создаете подкласс от своего одноразового ресурса, а подкласс содержит свои собственные ресурсы и уникальные методы, все становится немного сложнее.В большинстве примеров показано, как переопределить Dipose(bool disposing) правильно, но не выходите за рамки обработки ObjectDisposedException.

В этой ситуации у меня есть два вопроса.


Первый:

И подкласс, и базовый класс должны иметь возможность отслеживать состояние удаления.Я знаю несколько основных вариантов:

  • 1) Объявить частное bool удаленным;в обоих классах.Каждый класс отслеживает свой собственный this.dispose и генерирует его по мере необходимости.

  • 2) Используйте protected bool Dispose { get;частный набор;} вместо поля.Это позволит подклассу проверить удаленное состояние.

  • 3) Предоставьте некоторый защищенный вспомогательный метод для проверки удаленного состояния и выдайте его, извлекая имя текущего типа через отражение, если объект удален.

Преимущества и недостатки, которые я вижу для каждого варианта:

  • 1) Для меня это «пахнет», поскольку содержит дублированные логические значения, но, похоже, работает нормально.Я часто использую это при создании подклассов другого кода.

  • 2) При этом удаляются дублированные логические значения, но книги с рекомендациями по проектированию написаны не так и т. д.Однако это то, что я обычно использую, поскольку оно сохраняет единую точку для состояния.

  • 3) Мне кажется, это самый чистый вариант, но его нет в стандартных рекомендациях.От пользователей этого класса можно ожидать немного меньшего подхода, чем от других.

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


Второй:

При бросании ObjectDisposedException, что вы используете в качестве аргумента имени?Я знаю, что «типичный» вызов метода:

throw new ObjectDisposedException(GetType().FullName);

Есть комментарий на этой странице от сотрудника Microsoft, предположившего, что использование полного имени конкретного класса является подходящим.

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

Я не думаю, что это хорошая идея, но я столкнулся с этим в коде, написанном кем-то другим.Есть ли преимущества или недостатки у возврата имени класса, реализующего метод?

Это было полезно?

Решение

Обычно я реализую первый вариант.Действительно, похоже, это то, что рекомендуют рекомендации по проектированию.Причина не сразу очевидна, но, тем не менее, я считаю ее хорошей:любой реализатор класса должен иметь такую ​​же защиту от случая, когда объект удаляется как обычный потребитель.Другими словами, лучше этого не делать. предполагать что тот, кто реализует производный класс, точно знает, когда он может вызвать определенный метод, успешное выполнение которого может зависеть, а может и не зависеть от того, был ли объект уже удален или нет (хотя в идеале это все равно должно быть задокументировано с помощью комментариев XML).

Что касается вашего второго вопроса, я бы снова придерживался рекомендуемой практики передачи GetType().FullName, тем более что он используется в основной платформе .NET.Даже если вы считаете, что альтернативные методы более подходят, я считаю, что ради единообразия лучше придерживаться метода, используемого в .NET Framework.

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

Другие советы

Объявить частное bool удаленным;в обоих классах.Каждый класс отслеживает свой собственный this.dispose и генерирует его по мере необходимости.

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

Используйте protected bool Dispose { get;частный набор;} вместо поля.Это позволит подклассу проверить удаленное состояние.

Почему бы не сделать это общедоступным и вместо этого назвать IsDispose?Тогда вы будете делать то же самое, что и System.Windows.Forms.Control.Это хорошее решение, когда вы можете изменить базовый класс.

вы потенциально можете вернуть имя класса, который определяет метод

Нет.В примере кода, на который вы ссылаетесь, используется «GetType().FullName».Это всегда имя наиболее производного типа, а не типа, реализующего конкретный метод.

Я бы хотел, чтобы базовый класс объявил целочисленный флаг IsDispose, а затем заблокировал внешний метод «Dispose()». Перед началом удаления замените его на «1» (если перед обменом он не был равен нулю, пропустите утилизировать).Поддержка Finalize должна быть немного сложнее, но классы, реализующие Finalize, не должны быть производными от классов, отличных от Object, и — за исключением специализированных или служебных классов, таких как SafeHandle — не должны наследоваться.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top