суббота, 16 июля 2011 г.

Динамические методы и inherited

Это перевод Dynamic methods and inherited. Автор: Hallvard Vassbotn.

В предыдущем сообщении блога мы рассмотрели, как работают виртуальные методы и вызовы унаследованных методов. В Delphi есть и другой вид полиморфных методов: динамические методы (dynamic methods). Заметим, что эта серия постов про полиморфизм говорит только про Native Win32, но достаточно упомянуть, что в Delphi для .NET динамические методы, на самом деле, идентичны виртуальным методам. В Win32 методы сообщений (message methods) используют те же базовые структуры компилятора и механизм диспетчеризации, что и динамические методы, в то время как в .NET используется решение на базе атрибутов и отражения (reflection). Мы рассмотрим методы сообщений в следующем посте.

Хотя методы сообщений могут быть очень полезны сами по себе, динамические методы первоначально были созданы, чтобы обойти проблемы с размером сегмента в 16-битных ОС Windows и DOS. Впервые они появились в Turbo Pascal для Windows, а затем и в Borland Pascal 7.0 (который был нацелен на DOS реального режима, защищенный режим в DOS и 16-битные Windows). Проблема, которую они решали, заключалась в общем размере VMT всех классов. В те дни структуры VMT укладывались вместе в глобальный сегмент данных размером 64 Кб. И если у вас был базовый класс с большим числом виртуальных методов, а также большое число потомков этого класса, которые переопределяли только некоторые из этих методов, то у вас "терялась" часть пространства VMT. Это потому, что не замещённые методы всё ещё будут занимать слот в VMT таблице каждого потомка - и все эти слоты будут ссылаться на методы базового класса. Теперь, используя динамические методы, компилятор вместо этого строит своего рода разрежённый массив для каждого класса - динамическую таблицу методов (Dynamic Methods Table - DMT) - на которую есть ссылка из VMT. Только новые или реально замещённые (динамические) методы занимают место в DMT каждого класса. В большой иерархии классов, вроде Turbo Vision, OWL и VCL со многими потомками (подумайте о TComponent и TControl), использование динамических методов может сэкономить немного места. В дни ограничения в 64 Кб на сегмент данных это имело решающее значение.

В современном же мире их полезность весьма сомнительна. В Delphi для Win32 VMT и DMT структуры хранятся в сегменте кода, а не в сегменте данных; и нет никаких ограничений на их размер (ну, кроме естественных в 2-4 Гб для 32-битных приложений). Вызов динамического метода может быть значительно медленнее, чем вызов виртуального метода, и экономия места ничтожна по сравнению с характерным размером VCL приложения. В самом деле, динамические методы создают больший и более медленный код, но никак не меньший. Они экономят данные, но не код. Так что общий совет по ним на сегодня: избегайте динамических методов в классах - используйте виртуальные методы вместо них.

Ok, с этим небольшим уроком истории, мы готовы погрузиться в семантику динамического метода. Ну, это, на самом деле, не такое уж глубокое погружение. Семантика идентична работе виртуальных методов - как в отношении декларирования и переопределения методов, так и в использовании синтаксиса неявного inherited; по сравнению с явным inherited MethodName;.

Но для полноты - вот короткая история:
  TShape = class
    procedure Clicked; dynamic;
  end;
Это объявление нового динамического метода. Как уже было указано выше, вы должны дважды подумать, прежде чем делать это. По крайней мере: убедиться, что то, что вы делаете динамическим методом, не используется в чувствительном к производительности коде. Метод, вызываемый как часть обработки UI, вероятно, вполне сгодится - в примере ниже это обработка щелчка мыши по объекту.
  TRectangle = class(TShape)
    procedure Clicked; override;
  end;

  procedure TRectangle.Clicked;
  begin
    inherited Clicked;
    ShowMessage('Ouch!');
  end;
Этот код безусловно вызывает унаследованный метод Clicked в базовом классе. Если метод базового класса является абстрактным, этот код возбудит исключение EAbstractError во время выполнения.

Альтернативный синтаксис - просто использовать inherited; – к примеру:
procedure TRectangle.Clicked;
begin
  inherited;
  ShowMessage('Ouch!');
end;
Если родительский метод не является абстрактным, то это код будет работать идентично предыдущему - передавая все параметры, которые принимает текущий метод. Если же метод базового класса является абстрактным, то вызов inherited; становится no-op: компилятор не генерирует для него кода (и, следовательно, вы не можете установить на нём точку останова).

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

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

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

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку (поддерживается OpenID).

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.