如何构建通用 class 来管理对象类型的不同过程

How can I build a Generic class to manage different procedure of object types

我有很多处理 procedure of objectfunction 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 objectss 并处理公共代码,但我们将不得不重新引入一些依赖于原型的例程。

当然,例程的参数可以是不同的类型,并且这些可以通用,但我们不能使参数的数量或它们的顺序通用。

我实现了使用泛型 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;