Коллеги, я не вполне понимаю одну из рекомендаций в .NET design guidelines
В ней говорится:
DO prefer protected accessibility over public accessibility for virtual members. Public members should provide extensibility (if required) by calling into a protected virtual member.
The public members of a class should provide the right set of functionality for direct consumers of that class. Virtual members are designed to be overridden in subclasses, and protected accessibility is a great way to scope all virtual extensibility points to where they can be used.
то есть
Предпочитайте делать виртуальные члены (например, методы) защищёнными, а не открытыми. Открытые члены класса должны обеспечивать расширяемость (если нужно) путём вызова защищённого виртуального члена.
Открытые члены класса должны давать нужную, правильную функциональность для клиентского кода. Виртуальные члены разрабатываются чтобы быть переопределёными в классах-потомках, и защищённый доступ — хороший метод для того, чтобы ограничить видимость мест для расширения только теми, кто будет её использовать.
Прочитав этот текст, я всё же не понимаю, какая проблема может быть в том, если семейство виртуальных функций будет объявлено открытым. Например, вот в таком коде:
class Human : IDisposable
{
IDisposable property = new Property();
public virtual Dispose()
{
property.Dispose();
}
}
class Spy : Human
{
IDisposable spyGadgets = new SpyGadgets();
public virtual Dispose()
{
base.Dispose();
spyGadgets.Dispose();
}
}
Какие могут быть проблемы с таким кодом? О чём меня пытается предупредить документация? Если с этим случаем всё хорошо, то в каком случае возможны проблемы?
Приведите, если можно, осмысленный пример с кодом (не с классами Foo и Bar).
Огромное спасибо за ответы! Мне было нелегко выбрать, какой из них отметить галочкой, потому что все ответы очень хороши, и проливают свет на проблему с разных сторон.
Ответ
Это же рекомендации для разработчиков фреймворков. Очевидно, что разработчики фреймворков будут выпускать новые версии своих фреймворков. Также понятно, что одной из важнейших задач для них - сохранение обратной совместимости, по мере возможностей. А значит, что клиентов, что используют их код, следует максимально ограничить. То есть, это мы (ну или только я такой рукожоп) привыкли писать классы наследуемыми - но если мы пишем фреймворк, то нужна достаточно веская причина сделать класс наследуемым. Также нужна веская причина сделать метод виртуальным. Но вот ты сделал публичный метод виртуальным, и теперь клиенты могут наследоваться, перегрузить метод и запускать сами написанный ими же код, используя наш фреймвок - и мы уже не можем это контролировать. Я к тому, что делая публичный метод виртуальным, мы даем право клиенту решать, что будет делать наш АПИ и мы уже никак не можем ничего изменить, не сломав обратную совместимость.
Однако, сделав защищенный метод виртуальным, мы не гарантируем клиенту, что этот метод не перестанет использоваться в будущем, если наш публичный АПИ будет изменен. Таким образом клиенты, что перегрузили защищенный метод, сохраняют обратную совместимость, даже если логика публичных методов была изменена.
Походу надо добавить пример, хотя я и не мастер примеров :) Допустим есть следующие классы:
public class CsvWriter1
protected virtual void WriteInternal(IEnumerable
public void Write(IEnumerable
public class CsvWriter2
public virtual void Write(IEnumerable
Теперь представим, что в следующей версии нашего чудесного фреймворка нам надо писать объекты в CSV без заголовка. То есть, заголовок больше не нужен. И переопределять это больше нельзя. Что делать? Для класса CsvWriter1 все просто
public class CsvWriter1
protected virtual void WriteInternal(IEnumerable
protected void WriteInternalNew(IEnumerable
public void Write(IEnumerable
В случае с классом CsvWriter2 мы в луже. Так как указанное изменение для него будет обрано несовместимым, и конечно сломает логику клиентских классов.
Комментариев нет:
Отправить комментарий