Порядок вызова перекрытых методов
Порядок вызова перекрытых методов в случае множественного наследования рассмотрим на таком примере. Пусть в платформе определен ClassA -- пользовательский справочник. От него наследуется ClassB. Для этих классов перекрыт метод DoBeforeOpen c вызовом Inherited. У нас на руках экземпляр класса ClassB, который мы пытаемся открыть (Open), в процессе чего происходит вызов метода DoBeforeOpen. Тогда последовательность выполнения программного кода будет выглядеть следующим образом:
1 -- ClassA и ClassB -- это исключительно порождения платформы о которых Делфи ничего не известно. Для системы объект имеет тип TgdcAttrUserDefined.
2 -- Поскольку для TgdcAttrUserDefined нет метода DoBeforeOpen, через VMT попадаем на метод родителя -- TgdcBase.DoBeforeOpen.
3 -- Тело метода начинается с кода, который отвечает за вызов перекрытых методов в платформе. На вставке ниже, строки, начинающиеся с комментария:
procedure TgdcBase.DoBeforeOpen; {@UNFOLD MACRO INH_ORIG_PARAMS(VAR)} {M}VAR {M} Params, LResult: Variant; {M} tmpStrings: TStackStrings; {END MACRO} begin {@UNFOLD MACRO INH_ORIG_WITHOUTPARAM('TGDCBASE', 'DOBEFOREOPEN', KEYDOBEFOREOPEN)} {M} try {M} if (not FDataTransfer) and Assigned(gdcBaseMethodControl) then {M} begin {M} SetFirstMethodAssoc('TGDCBASE', KEYDOBEFOREOPEN); {M} tmpStrings := TStackStrings(ClassMethodAssoc.IntByKey[KEYDOBEFOREOPEN]); {M} if (tmpStrings = nil) or (tmpStrings.IndexOf('TGDCBASE') = -1) then {M} begin {M} Params := VarArrayOf([GetGdcInterface(Self)]); {M} if gdcBaseMethodControl.ExecuteMethodNew(ClassMethodAssoc, Self, 'TGDCBASE', {M} 'DOBEFOREOPEN', KEYDOBEFOREOPEN, Params, LResult) then exit; {M} end else {M} if tmpStrings.LastClass.gdClassName <> 'TGDCBASE' then {M} begin {M} Inherited; {M} Exit; {M} end; {M} end; {END MACRO} if (not CanView) and (not IBLogin.IsUserAdmin) then begin if SubType > '' then raise EgdcUserHaventRights.CreateFmt(strHaventRights, [strView, ClassName, SubType, GetDisplayName(SubType)]) else raise EgdcUserHaventRights.CreateFmt(strHaventRightsShort, [strView, ClassName]) end; if not FSQLInitialized then InitSQL; if HasSubSet('ByID') and (FID > -1) then ParamByName(GetKeyField(SubType)).AsInteger := FID else if HasSubSet('ByName') and (FObjectName > '') then ParamByName(GetListField(SubType)).AsString := FObjectName; inherited DoBeforeOpen; {@UNFOLD MACRO INH_ORIG_FINALLY('TGDCBASE', 'DOBEFOREOPEN', KEYDOBEFOREOPEN)} {M} finally {M} if (not FDataTransfer) and Assigned(gdcBaseMethodControl) then {M} ClearMacrosStack2('TGDCBASE', 'DOBEFOREOPEN', KEYDOBEFOREOPEN); {M} end; {END MACRO} end;
Именно здесь вступает в силу Подтип нашего объекта и система начинает понимать, что мы имеем дело с экземпляром TgdcAttrUserDefinedClassB, а не TgdcAttrUserDefined. В базе данных отыскивается скрипт-функция перекрытого метода, загружается в скрипт-контрол и запускается на выполнение.
4 -- По магическому слову Inherited мы снова оказываемся в движке платформы. Для класса TgdcAttrUserDefinedClassB будет найден родитель -- TgdcAttrUserDefinedClassA. Для него загружена скрипт-функция перекрытого метода и запущена на выполнение.
5 -- Снова вызов Inherited из скрипт-функции. На этот раз родитель -- делфовский класс TgdcAttrUserDefined. Для него нет метода DoBeforeOpen и мы возвращаемся в TgdcBase.DoBeforeOpen. Служебный код, отвечающий за связь Делфи с VBScript, закончился. Начинается логика метода DoBeforeOpen.
6, 7 -- По вызову inherited перейдем на уровень родителя -- TCustomIBDataSet.DoBeforeOpen. По завершении -- вернемся обратно.
8 -- Собственно, логика метода DoBeforeOpen закончилась. Снова служебный код, который сращивает статичный, однажды откомпилированный код Делфи и динамичный, интерпретируемый код из VBScript. Возращаемся в место вызова -- TgdcAttrUserDefinedClassA.DoBeforeOpen, сразу после слова Inherited.
9 -- Когда выполнение скрипт-функции перекрытого метода закончится, движок платформы вернет нас к прерванному TgdcAttrUserDefinedClassB.DoBeforeOpen.
A, B, C -- Скрипты закончились. Завершаем метод TgdcBase.DoBeforeOpen. Затем, минуя несуществующий TgdcAttrUserDefined.DoBeforeOpen, возвращаемся к продолжению программы.