Delphi: назначение обработчика тестовых событий
Я хочу назначить обработчик событий в конструкторе, если он не имеет назначенного. Следовательно, я хочу удалить в конечном итоге назначенный обработчик событий в деструкторе. Я написал код следующим образом, но он не может быть скомпилирован.
constructor TSomeControl.Create(Panel: TPanel);
begin
inherited Create;
FPanel := Panel;
if not Assigned(FPanel.OnResize) then
FPanel.OnResize := HandlePanelResize;
end;
destructor TSomeControl.Destroy;
begin
if @FPanel.OnResize = @HandlePanelResize then // [dcc32 Error] E2036 Variable required
FPanel.OnResize := nil;
FPanel := nil;
inherited;
end;
Как проверить его правильно? Я знаю, что решение заключается в использовании переменной для записи, назначил ли я OnResize
. Но я не хочу, чтобы это было решением.
3 ответов
нет необходимости писать здесь пользовательский код, так как вы можете использовать уже существующие компараторы из Generics.Defaults
:
destructor TSomeControl.Destroy;
begin
if Assigned(FPanel) and TEqualityComparer<TNotifyEvent>.Default.Equals(
FPanel.OnResize, HandlePanelResize) then
FPanel.OnResize := nil;
FPanel := nil;
inherited;
end;
это осложняется тем, что OnResize
является свойством, а не переменной. И довольно сложно ссылаться на метод напрямую без того, чтобы компилятор не думал, что вы хотите вызвать метод. Это большой недостаток удобства Паскаля, позволяя вам вызывать процедуру без использования parens.
все это делает его довольно трудно сделать это в одной строке. Насколько я вижу, вам нужно сделать что-то вроде этого:
destructor TSomeControl.Destroy;
var
Method1, Method2: TNotifyEvent;
begin
if Assigned(FPanel) then
begin
Method1 := FPanel.OnResize;
Method2 := HandlePanelResize;
if TMethod(Method1) = TMethod(Method2) then
FPanel.OnResize := nil;
end;
FPanel := nil;
inherited;
end;
это зависит от современного Делфи TMethod
запись, которая включает перегруженный оператор равенства, чтобы сделать =
испытательные работы.
Я бы завернул все это в общий метод, если бы я делал это более одного раза. Это может выглядеть так:
type
TEventComparer = class
class function Equal<T>(const lhs, rhs: T): Boolean; static;
end;
class function TEventComparer.Equal<T>(const lhs, rhs: T): Boolean;
begin
Assert(SizeOf(T)=SizeOf(TMethod));
Result := TMethod((@lhs)^)=TMethod((@rhs)^);
end;
вы бы назвали это так:
destructor TSomeControl.Destroy;
begin
if Assigned(FPanel) and TEventComparer.Equal<TNotifyEvent>(FPanel.OnResize,
HandlePanelResize) then
FPanel.OnResize := nil;
FPanel := nil;
inherited;
end;
одна вещь, которую это подчеркивает, заключается в том, что общие ограничения, доступные вам, не позволяют ограничивать тип указателем метода. Следовательно, основной здравомыслие проверить, что размер T
совпадает с размером метода. Это не предложение безопасность. Вы можете вызвать этот метод, передав Int64
или Double
. Мне было бы интересно посмотреть, может ли кто-нибудь придумать более чистый вариант.
нет необходимости использовать Generics.Defaults
или любые дженерики вообще. Есть TMethod
запись объявлена в System
единица, так что это, вероятно, самый простой:
destructor TSomeControl.Destroy;
var
Event: TNotifyEvent;
begin
Event := HandlePanelResize;
if TMethod(FPanel.OnResize).Code = Addr(Event) then
FPanel.OnResize := nil;
FPanel := nil;
inherited;
end;