Из блога:
То, что делает эта функция, удаляет
сам и непосредственный абонент из
Цепочка вызовов и прямые переводы
управление соответствующим "небезопасным"
метод при сохранении переданного в
Параметр (ы).
Код исключает кадр стека для InternalAdd, который имеет только один параметр, Self
. Он не влияет на событие, которое вы передали, и поэтому безопасно копировать для любой другой функции только с одним параметром и регистр соглашение о вызовах.
РЕДАКТИРОВАТЬ: В ответ на комментарий, есть точка, которую вы упускаете. Когда вы написали: «Я знаю, что делает код (очищает фрейм стека от родительского вызова)», вы ошиблись. Он не касается родительского вызова. Он не очищает кадр стека от Add, он очищает кадр стека от текущего вызова, InternalAdd.
Вот немного базовой теории ОО, так как вы, кажется, немного запутались в этом вопросе, который, я признаю, немного запутывает. Add не действительно имеет один параметр, а SetEventDispatcher не имеет двух. На самом деле их два и три соответственно. Первый параметр любого вызова метода, который не объявлен static , равен Self
, и он незаметно добавляется компилятором. Таким образом, каждая из трех внутренних функций имеет один параметр. Вот что я имел в виду, когда писал это.
Код Аллена работает вокруг ограничения компилятора. Каждое событие является указателем на метод, но для обобщений нет «ограничения метода», поэтому компилятор не знает, что T всегда будет 8-байтовой записью, которую можно привести к TMethod. (На самом деле, это не обязательно. Вы можете создать TMulticastEvent<byte>
, если вы действительно хотите сломать вашу программу новыми и интересными способами.) Внутренние методы используют ассемблер, чтобы вручную эмулировать типизацию, удаляя себя из полностью вызвать стек и JMPing (в основном GOTO) к соответствующему методу, оставив в нем тот же список параметров, что и у вызывающей его функции.
Итак, когда вы видите
procedure TMulticastEvent.Add(const AMethod: T);
begin
InternalAdd;
end;
то, что он делает, эквивалентно следующему, если он будет компилироваться:
procedure TMulticastEvent.Add(const AMethod: T);
begin
Add(TEvent(AMethod));
end;
Ваш InternalSetDispatcher захочет сделать то же самое: убрать свой собственный вызов с одним параметром, а затем перейти к SetDispatcher с точно таким же списком параметров, как у вызывающего метода SetEventDispatcher. Не имеет значения, какие параметры имеет вызывающая функция или какая функция используется. Что имеет значение (и это важно!), Так это то, что SetEventDispatcher и SetDispatcher имеют одинаковую подпись вызова друг с другом.
Так что да, гипотетический код, который вы разместили, будет работать нормально и не повредит стек вызовов.