Почему ключевое слово «sealed» существует в .Net? - PullRequest
12 голосов
/ 17 февраля 2009

Большое количество классов в .Net Framework помечены как «запечатанные», что не позволяет вам наследовать эти классы своими собственными. Конечно, это идет вразрез с природой объектной ориентации, где вы можете расширить и переопределить поведение существующих объектов.

Есть ли веская причина для существования ключевого слова «загерметизировано»?

Например, NotifyCollectionChangedEventArgs in Silverlight запечатан. я хотел создать свою собственную версию ObservableCollection, которая поддерживается AddRange и RemoveRange, но Silverlight версия NCCEA не предоставить конструктор, который поддерживает несколько элементов для NewItems и Свойства OldItems, которые уже определены как ILists. Обычно я бы просто расширить класс с моим собственным вариантом которые отвергли NewItems и Свойства OldItems, но в этом случае Я не могу и не вижу причин, почему так и должно быть.

Ответы [ 9 ]

21 голосов
/ 17 февраля 2009

Создание классов (или фреймворков) для расширения не является тривиальным, а просто наследование - не единственный принцип объектно-ориентированного программирования.

Итак, sealed существует, чтобы позволить разработчику / конструктору выразить и сохранить эти намерения. Запечатывание класса может также облегчить их жизнь, уменьшая бремя обслуживания. Это позволяет первоначальному разработчику контролировать, как расширяется класс (или фреймворк), чтобы они могли вносить внутренние изменения, не беспокоясь о внесении изменений в чужой код.

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


Ссылка: Эрик Липперт - Почему запечатано так много базовых классов?

6 голосов
/ 17 февраля 2009

Этот ответ на несколько связанный вопрос, который я задал сегодня, может помочь прояснить цели закрытия класса:

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

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

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

Лисковская субстанция и состав

Теперь перейдите по этой ссылке и добавьте фактического автора.

2 голосов
/ 17 февраля 2009

Не копаясь слишком глубоко, поймите, что Microsoft предпочитает закрывать класс, когда есть потенциальные проблемы безопасности, ремонтопригодности или обратной совместимости, с которыми ему придется столкнуться в последующем. Например, System.String запечатан из соображений безопасности и производительности.

В этом конкретном случае вам нужно будет спросить разработчика Microsoft, почему они решили запечатать этот класс. Тем не менее, литература по архитектурному руководству, которую я читал в последнее время, склоняется к подходу «печать, если вы не знаете , его нужно будет расширить». Эта литература стремится поддерживать использование методов расширения, где это возможно. (Я не говорю, что согласен с этим; я просто говорю, что это то, что я читал в последнее время.)

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

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

2 голосов
/ 17 февраля 2009

Краткий ответ, потому что Microsoft так сказала. Более длинный ответ заключается в том, что Microsoft предоставила механизм расширения закрытых классов, называемый методами расширения.

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

Вы также должны рассмотреть, что на самом деле является наследованием. Это не просто способ изменить класс, он также обеспечивает полиморфизм. Что если вы измените семантику, скажем, строкового класса, то передадите свой новый строковый класс объекту, который ожидает, что строка будет действовать определенным образом? В сущности, sealed обеспечивает соблюдение договора о том, что этот объект всегда будет работать так, как вы ожидаете.

1 голос
/ 17 февраля 2009

Я хотел бы предположить, что нет никаких веских причин для запечатывания классов, кроме защиты невежественного -err, я имею в виду невиновность. Вы знаете старую поговорку: «Дай им достаточно веревки, и они повесят себя». Пусть они покачиваются, говорю я. Возможно, это мой опыт работы с C ++, но мне вполне комфортно знать, что у меня есть сила, чтобы полностью набрать вещи, если я не прилежен.

Я склонен программировать интерфейсом. Интерфейсы, конечно, общедоступны, и любой может предоставить свою собственную реализацию, соответствующую договору, указанному в интерфейсе. Мои конкретные классы, которые реализуют эти интерфейсы, имеют тенденцию быть частной проблемой и помечены как внутренние и / или частные. Я не чувствую, что мне нужно запечатывать такие классы.

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

Запечатывание также может быть допустимо для типов, которые считаются типами старых данных, но я не уверен в этом.

1 голос
/ 17 февраля 2009

Хотя я и согласен с тем, что .NET, вероятно, слишком сильно запечатывает, обычно это защищает целостность экосистемы. Если одним из ваших главных приоритетов является обеспечение стабильности всей платформы / API / среды выполнения и наличие некоторой хрупкой взаимозависимости между классами, может быть безопаснее не допустить, чтобы люди игнорировали это поведение и непреднамеренно дестабилизировали основные функции.

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

0 голосов
/ 27 июля 2013

Одно из практических применений, где я нашел полезное запечатанное ключевое слово, состоит в том, чтобы избежать "Хрупких проблем класса". Вот видео, которое показывает одну из уязвимых проблем базового класса http://www.youtube.com/watch?v=xnA3RUJcyY4

Позвольте мне объяснить немного подробнее.

Рассмотрим приведенный ниже сценарий, в котором у нас есть родительский класс, называемый «DbParent», с виртуальным методом «Insert».

класс DbParent {

    public virtual void Insert()
    {
        Console.WriteLine("Parent insert");
    }

}

Ниже приведен простой дочерний класс со своей собственной реализацией «Вставка», которая устанавливается в различных местах.

  class DbChildClass : DbParent
  {
  public void  Insert()
    {
        Console.WriteLine("Child inserts");
    }

}

Теперь, скажем, после нескольких месяцев развертывания разработчики родительских классов, не понимая влияния на дочерние классы, идут и добавляют новый метод «Добавить». Этот метод «Add» вызывает метод «Insert» для внутреннего использования (ниже приведен фрагмент кода для того же самого).

  class DbParent
  {
  public  void Add() // Adds method with out consulting
  {
  this.Insert();
  }

  public virtual void Insert()
  {
        Console.WriteLine("Parent insert");
  }
  }

Теперь клиентские программы, которые вызывают метод «Add» путем создания объектов дочернего класса, ожидают, что должна быть вызвана реализация «Insert» класса «Child».

  DbChildClass o = new DbChildClass();
  o.Add();
  Console.Read();

Но, чёрт возьми, если вы запустите приведенный ниже код, вы увидите, что родительский класс «Вставить» называется, который НЕ ОЖИДАЕТСЯ.

Поэтому я бы пошел и пометил класс как «Запечатанный», чтобы избежать подобных проблем.

0 голосов
/ 17 февраля 2009

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

Хорошая аналогия - дверь и замок. Природа двери - позволить людям проходить через нее, для этого она и создана. Тем не менее, большинство дверей спроектированы с замками, которые могут ограничивать способность людей проходить через них. Решение о том, имеет ли дверь замок и является ли этот замок по умолчанию, остается за архитектором комнаты, исходя из того, что находится в комнате и кто должен иметь к ней доступ, а не по факту, что это дверь.

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

0 голосов
/ 17 февраля 2009

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

Запечатанные классы имеют ряд преимуществ:

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

В противном случае, если вы хотите украсить запечатанный класс «комфортными» методами, используйте методы расширения (C # 3.0).

...