Страницы

Поиск по вопросам

понедельник, 8 октября 2018 г.

Почему в .Net много запечатаных классов?

По каким причинам большинство .net классов являются запечатанными (например: Int32, Double, String и т. п.)?
Есть ли в каких книгах/статьях объяснение данного архитектурного решения от создателей платформы?


Ответ

Все классы, которые не поддерживают наследование от них, должны быть sealed. Чтобы класс поддерживал наследование от него, необходимо обдуманное архитектурное решение: нужно рассмотреть реальные сценарии, когда это требуется; продумать, какие члена будут перегружаться; как остальные части системы будут работать с унаследованными классами и так далее.
Если все классы сначала делать незапечатанными, а потом думать, то велика вероятность, что от этого будет больше вреда, чем пользы: никакие реальные возможности добавлены не будут, программисты полезут в потроха класса и нагромоздят хаков. И все эти хаки придётся поддерживать, потому что обратная совместимость — это святое. Это значит, что реализация может быть закрыта для улучшений в будущих версиях.
Если какой-то класс закрыт, это значит, что архитектор не нашёл нормальных сценариев наследования, которые оправдывали бы незапечатенность класса.
Классы типа String и StringBuilder закрыты, потому что это открывает возможности для изменения реализации классов, производительность которых критична для системы. Например, раньше билдер работал по принципу List, теперь он реализован через связный список блоков, каждый из которых растёт как List. Если бы хоть одна деталь старой реализации просочилась в protected интерфейс, изменить её было бы невозможно. Если бы внутренняя реализация была закрыта, то наследование было бы абсолютно бесполезно: все операции и так были бы публичными. В этом случае достаточно статических методов (в том числе extension методов).
Что касается Int32 и Double, то это вообще-то структуры, они вообще не поддерживают наследование.
Логика разработчиков .NET описана в книге Framework Design Guidelines. Некоторые части книги доступны на MSDN

На этот вопрос отвечал Эрик Липперт в статье Why Are So Many Of The Framework Classes Sealed? Он называет один ключевой принцип: "Хороший код делает ровно то, для чего он создан, ни больше ни меньше", а затем раскрывает его:
Философский. Наследование основано на отношении is-a. Если нельзя придумать такой класс, то наследование запрещено. Практический. Создавать классы, которые можно расширять, сложно. Совместимость. Закрытый класс можно сделать открытым, но не наоборот. Безопасность. Если с помощью наследования можно переопределить логику базового класса, можно исковеркать логику и подвергнуть систему угрозе.
Более развёрнутая аргументация по ссылке выше.

В целом, классы во фреймворке "по умолчанию" закрыты примерно по той же причине, почему методы в C# по умолчанию невиртуальны. Можно вспомнить Java с виртуальными по умолчанию методами и учиться на ошибках.

Комментариев нет:

Отправить комментарий