delphi - 如何构建一个通用类来管理对象类型的不同过程
问题描述
我有很多处理列表procedure of object
或function of object
类型的通用代码。我想将此代码标准化为通用类,以便共享公共代码。
如果我有:
type
TNotifyEvent = procedure (Sender: TObject) of object;
TNotifyIntEvent = procedure (Sender: TObject; Value: Integer): Boolean of object;
然后我可以拥有:
type
TCallbackList = class(TList<TNotifyEvent>)
end;
TIntCallbackList = class(TList<TIntNotifyEvent>)
end;
但是由于我在这里有更多的代码而不仅仅是声明一个列表,我希望能够像这样将整个类声明为 Generic:
type
TFunctionManager<T: procedure of object> = class(TList<T>)
public
procedure Subscribe(fnCallback: T);
procedure FireCallbacks();
procedure Unsubscribe(fnCallback: T);
// ... and so on
end;
但是泛型不允许将泛型类型参数限制为procedure of object
有没有办法实现这种功能?
解决方案
我找到了一种方法来实现我所需要的。考虑问题很明显,拥有procedure of object
类型的全部意义在于您可以调用它。要调用它,您需要知道函数的原型,因此使用函数原型的泛型的基本前提过于局限。
为了说明这一点:在问题的示例中,如果方法FireCallbacks
调用列表中的所有方法,那么如果回调的原型发生更改,则FireCallbacks
需要更改的原型。因此,TFunctionManager<TNotifyIntEvent>
我需要声明procedure FireCallbacks(Value: Int);
以便我有足够的参数来调用例程。
由于procedure of object
类型存储在内存中,TMethod
无论方法的原型是什么,我们都可以构建一个通用类来存储procedure of objects
s 并处理公共代码,但我们将不得不重新引入一些依赖于原型的例程。
当然,例程的参数可以是不同类型的,并且可以将它们设为通用,但我们不能将参数的数量或它们的顺序设为通用。
我实现了使用通用类来实现通用代码的目标,支持不同的函数原型,方法是实现基类TNotifyEvent
(任何procedure of object
类型都可以,但这对我的要求很有意义),然后使用泛型类型的派生类来支持具有不同签名的回调。
下面的代码提供了我如何实现它的基本原理(没有显示所有代码,只是与在函数原型上使用泛型相关的位,但如果你想这样做,它应该足以让你开始):
interface
type
TCallbackManager = class(TObject)
protected
_pCallbacks: TList<TNotifyEvent>;
public
constructor Create();
destructor Destry(); override;
procedure FireCallbacks(); virtual;
procedure Subscribe(fnCallback: TNotifyEvent); virtual;
procedure Unsubscribe(fnCallback: TNotifyEvent); virtual;
end;
T1ParamCallback<TParam1> = procedure(Sender: TObject; p1: TParam1) of Object;
T1ParamCallbackManager<TParam1> = class(TCallbackManager)
public
procedure FireCallbacks(p1: TParam1); reintroduce;
procedure Subscribe(fnCallback: T1ParamCallbackManager<TParam1>); reintroduce;
procedure Unsubscribe(fnCallback: T1ParamCallbackManager<TParam1>); reintroduce;
end;
T2ParamCallback<TParam1, TParam2> = procedure(Sender: TObject; p1: TParam1; p2: TParam2) of Object;
T2ParamCallbackManager<TParam1, TParam2> = class(TCallbackManager)
public
procedure FireCallbacks(p1: TParam1; p1: TParam2); reintroduce;
procedure Subscribe(fnCallback: T2ParamCallbackManager<TParam1, TParam2>); reintroduce;
procedure Unsubscribe(fnCallback: T2ParamCallbackManager<TParam1, TParam2>); reintroduce;
end;
implementation
{ TCallbackManager }
constructor TCallbackManager.Create;
begin
Self._pCallbacks:=TList<TNotifyEvent>.Create;
inherited;
end;
destructor TCallbackManager.Destroy;
begin
FreeAndNil(Self._pCallbacks);
inherited;
end;
procedure TCallbackManager.FireCallbacks();
var
fnCallback: TNotifyEvent;
begin
for fnCallback in Self._pCallbacks do
if(Assigned(fnCallback)) then
fnCallback(Self);
end;
procedure TCallbackManager.Subscribe(fnCallback: TNotifyEvent);
begin
if(not(Self._pCallbacks.Contains(fnCallback))) then
Self._pCallbacks.Add(fnCallback);
end;
procedure TCallbackManager.Unsubscribe(fnCallback: TNotifyEvent);
begin
Self._pCallbacks.Remove(fnCallback);
end;
{ T1ParamCallbackManager }
procedure T1ParamCallbackManager.FireCallbacks(p1: TParam1);
var
fnCallback: TNotifyEvent;
begin
for fnCallback in Self._pCallbacks do
if(Assigned(fnCallback)) then
T1ParamCallback<TParam1>(fnCallback)(Self, p1);
end;
procedure T1ParamCallbackManager.Subscribe(fnCallback: T1ParamCallback<TParam1>);
begin
inherited Subscribe(TNotifyEvent(fnCallback));
end;
procedure T1ParamCallbackManager.Unsubscribe(fnCallback: TNotifyEvent);
begin
inherited Unsubscribe(TNotifyEvent(fnCallback));
end;
{ T2ParamCallbackManager }
procedure T2ParamCallbackManager.FireCallbacks(p1: TParam1; p2: TParam2);
var
fnCallback: TNotifyEvent;
begin
for fnCallback in Self._pCallbacks do
if(Assigned(fnCallback)) then
T2ParamCallback<TParam1, TParam2>(fnCallback)(Self, p1, p2);
end;
procedure T2ParamCallbackManager.Subscribe(fnCallback: T2ParamCallback<TParam1, TParam2>);
begin
inherited Subscribe(TNotifyEvent(fnCallback));
end;
procedure T2ParamCallbackManager.Unsubscribe(fnCallback: T2ParamCallback<TParam1, TParam2>);
begin
inherited Unsubscribe(TNotifyEvent(fnCallback));
end;