如何构建通用 class 来管理对象类型的不同过程
How can I build a Generic class to manage different procedure of object types
我有很多处理 procedure of object
或 function of object
类型列表的常用代码。我想将此代码标准化为通用 class 以便共享通用代码。
如果我有:
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;
但是因为我这里有更多的代码而不只是声明一个列表,所以我希望能够将整个 class 声明为像这样的泛型:
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
存储在内存中,无论方法的原型是什么,我们都可以构建一个通用 class 来存储 procedure of objects
s 并处理公共代码,但我们将不得不重新引入一些依赖于原型的例程。
当然,例程的参数可以是不同的类型,并且这些可以通用,但我们不能使参数的数量或它们的顺序通用。
我实现了使用泛型 class 实现通用代码、支持不同函数原型的目标,方法是在 TNotifyEvent
上实现基础 class(任何 procedure of object
type 可以,但这对我的要求很有意义)然后派生 classes,具有泛型,以支持具有不同签名的回调。
以下代码提供了我如何实现它的基本框架(并未显示所有代码,仅显示与在函数原型上使用泛型相关的位,但它应该足够了如果您想这样做,可以帮助您入门):
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;
我有很多处理 procedure of object
或 function of object
类型列表的常用代码。我想将此代码标准化为通用 class 以便共享通用代码。
如果我有:
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;
但是因为我这里有更多的代码而不只是声明一个列表,所以我希望能够将整个 class 声明为像这样的泛型:
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
存储在内存中,无论方法的原型是什么,我们都可以构建一个通用 class 来存储 procedure of objects
s 并处理公共代码,但我们将不得不重新引入一些依赖于原型的例程。
当然,例程的参数可以是不同的类型,并且这些可以通用,但我们不能使参数的数量或它们的顺序通用。
我实现了使用泛型 class 实现通用代码、支持不同函数原型的目标,方法是在 TNotifyEvent
上实现基础 class(任何 procedure of object
type 可以,但这对我的要求很有意义)然后派生 classes,具有泛型,以支持具有不同签名的回调。
以下代码提供了我如何实现它的基本框架(并未显示所有代码,仅显示与在函数原型上使用泛型相关的位,但它应该足够了如果您想这样做,可以帮助您入门):
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;