Должен ли базовый класс в шаблоне IDisposable разрешать производным классам использовать свой удаленный флаг?

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

  •  21-08-2019
  •  | 
  •  

Вопрос

В настоящее время я работаю над исправлением кодовой базы С#, которая не имеет хорошего шаблона использования Dispose.

Это большая кодовая база, требующая ресурсов, и она использует множество пользовательских неуправляемых библиотек C++ на низком уровне.

Я хорошо понимаю шаблон утилизации.Я потратил некоторое время на понимание того, что я считаю статьей золотого стандарта по этому вопросу: Статья Джо Даффи об утилизации

Пытаясь свести к минимуму дублирование кода, мы рассмотрели некоторые вспомогательные классы удаления, поэтому мой вопрос:

Если базовый класс реализует стандартный шаблон Dispose, должен ли он разрешать совместное использование своего удаленного флага, т.е.отмечен как защищенный?

Чтобы уточнить, я имею в виду, должно ли быть только одно логическое состояние в иерархии наследования, которое определяет, был ли удален экземпляр объекта, или на каждом этапе лестницы наследования должно быть частное логическое значение?

Примеры в MSDN и по ссылке выше устанавливают флаг на каждом уровне, но никогда не объясняют причину этого.Я не уверен в этом вопросе, каковы ваши мысли и причины?

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

Решение

Я бы сказал, нет, он не должен использовать один и тот же флаг.Совместное использование флага создает возможности для сбоев, которые могла бы предотвратить лучшая инкапсуляция.

Например, рассмотрим сценарий, в котором у вас есть свойство Dispose, доступное только для чтения, в базовом классе большинства.Поле поддержки имеет значение true только в методе Dispose(dispositing) базового класса.Это означает, что свойство Dispose может возвращать значение true только в том случае, если вызывается базовый класс Dispose (конечно, за исключением злого отражения).Это позволяет базовому классу предоставлять юридически исполняемый контракт.

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

Я бы выбрал первый вариант, поскольку он обеспечивает наиболее обязательную силу контракта.

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

Я бы рекомендовал иметь свойство Dispose в базовом классе с общедоступным методом получения и защищенным методом установки.

Если у вас есть методы, которые должны делать что-то другое, когда класс был удален, например, выдавать исключение, я бы создал защищенное свойство-получатель только для базового класса, которое считывает частное поле базового класса.Таким образом, вы позволите любому наследнику узнать, способен ли он выполнить операцию.

Затем, чтобы узнать, был ли класс уже удален в свой собственный метод удаления (например:чтобы избежать двойного освобождения ресурсов), я думаю, что наличие частного флага лучше для ясности и обслуживания.

В дополнение к ответу JaredPar я бы добавил, что не всегда необходимо наличие _disposed флаг.Очень часто другие поля объекта, «связанные с ресурсами», естественным образом предоставляют способ представления удаленного состояния.

Например, внешний COM-объект, который необходимо Shutdown прежде чем он будет отброшен, будет представлен ссылкой на COM-объект, и поэтому соответствующая часть Dispose было бы:

if (_comObject != null)
{
    _comObject.Shutdown();
    _comObject = null;
}

Это можно безопасно запустить несколько раз без вызова Shutdown более одного раза, как это необходимо.Другие методы, которые пытаются использовать _comObject после Dispose получит NullReferenceException, или в идеале эти методы должны проверять поле _comObject и выдавать ObjectDisposedException.

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

Так что вряд ли это будет так полезно для производных классов, даже если это безопасная идея (а это не так, как объясняет ДжаредПар).

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