Может быть, есть другой способ?
Лучшим способом было бы изменить код, который ожидает, что TDelegate
также принимает i1
.Если вы написали код, изменение будет тривиальным, и в основном это лучшее, что вы можете сделать.Если вы не можете изменить код, ожидающий TDelegate
, и вам абсолютно необходимо вызвать процедуру из интерфейса, вам может потребоваться создать объект адаптера, что-то вроде этого:
TDelegateAdapter = class
private
Fi1: i1;
public
constructor Create(Ani1: i1);
procedure P;
end;
constructor TDelegateAdapter.Create(Ani1: i1);
begin
Fi1 := Ani1;
end;
procedure TDelegateAdapter.P;
begin
Fi1.P1;
end;
Затем вкод, где вам нужно назначить TDelegate, сделайте что-то вроде этого:
var Adapter: TDelegateAdapter;
Intf: i1; // assumed assigned
ObjectExpectingDelegate: TXObject; // assumed assigned
begin
Adapter := TDelegateAdapter.Create(Intf);
try
ObjectExpectingDelegate.OnSomething := Adapter.P;
try
ObjectExpectingDelegate.PerformWork;
finally ObjectExpectingDelegate.OnSomething := nil;
end;
finally Adapter.Free;
end;
end;
Edit
Если вы используете версию Delphi, которая поддерживает анонимные методы, вы можете реализовать адаптер Delegate, используя такиеанонимные методы, требующие только одного «адаптера» на сигнатуру процедуры.Delphi реализует анонимные методы за кулисами, используя интерфейсы, поэтому производительность во время выполнения будет хорошей, не нужно беспокоиться.
Приведенный ниже код является демонстрационной консольной реализацией анонимного адаптера делегата.Посмотрите прямо на последний блок begin
- end
, чтобы увидеть магию.
program Project29;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
// This is the type of the anonymous method I want to use
TNoParamsProc = reference to procedure;
// This implements the "delegate" adapter using an anonymous method
TAnonymousDelegateAdapter = class
private
NoParamsProc: TNoParamsProc;
public
constructor Create(aNoParamsProc: TNoParamsProc);
procedure AdaptedDelegate;
end;
{ TAnonymousDelegateAdapter }
procedure TAnonymousDelegateAdapter.AdaptedDelegate;
begin
NoParamsProc;
end;
constructor TAnonymousDelegateAdapter.Create(aNoParamsProc: TNoParamsProc);
begin
NoParamsProc := aNoParamsProc;
end;
// --------- test code follows ----------
type
// Interface defining a single method.
ISomething = interface
procedure Test;
end;
// Implementation of the interface above
TSomethingImp = class(TInterfacedObject, ISomething)
public
procedure Test;
end;
// Definition of delegate
TNoParamsDelegate = procedure of object;
{ TSomethingImp }
procedure TSomethingImp.Test;
begin
WriteLn('Test');
end;
// ---- Test program to see it all in action. ---
var intf: ISomething;
Dlg: TNoParamsDelegate;
begin
intf := TSomethingImp.Create;
// Here I'll create the anonymous delegate adapter, notice the "begin - end"
// in the constructor call; That's the anonymous method. Runtime performance
// of anonymous methods is very good, so you can use this with no warries.
// My anonymous method uses the "intf" variable and calls the method "Test"
// on it. Because of that the "intf" variable is "captured", so it doesn't run
// out of scope as long as the anonymous method itself doesn't run out of scope.
// In other words, you don't risk having your interface freed because it's reference
// count reaches zero. If you want to use an other interface, replace the code
// in the begin-end.
with TAnonymousDelegateAdapter.Create(procedure begin intf.Test; end) do
try
Dlg := AdaptedDelegate;
Dlg;
finally Free;
end;
Readln;
end.