方法指针有基类型吗?

时间:2015-08-20 05:27:27

标签: delphi

在下面的代码中,我定义了两种方法指针类型。我需要另一个接受任何这些类型的参数的过程,并根据其他值使用它。但是我找不到这两种类型的基类型能够将两者都传递到过程中:

type
  TIntProc: procedure (I: Integer) of object;
  TStrProc: procedure (S: string) of object;

....

  procedure TForm1.CallbackTest(A: Integer; Callback: procedure of object {What type should I use here?});
  var
    IntProc: TIntProc;
    StrProc: TStrProc;   
  begin
    if A = 1 then begin
      IntProc:= TIntProc(Callback);
      IntProc(100);
    end else begin
      StrProc:= TStrProc(Callback);
      StrProc('Hello');
    end;
  end;

  procedure TForm1.MyIntProc(I: Integer);
  begin
    ShowMessage(IntToStr(I));
  end;

  procedure TForm1.MyStrProc(S: string);
  begin
    ShowMessage(S);
  end;

我应该写:

CallbackTest(1, MyIntProc);

还有:

CallbackTest(2, MyStrProc);

但当然我在两者上都会出错,因为procedure of object用作第二个参数的类型不是procedure (I: Integer) of objectprocedure (S: string) of object的基本类型。任何想法如何做到这一点?

2 个答案:

答案 0 :(得分:3)

程序类型没有继承。您不能通过单个参数传递任何类型的类型安全方法。你需要施展。例如,您可以在程序类型变量和typecast之间TMethod

// Health warning, this code is for illustration, I do not endorse its use

procedure Foo(A: Integer; Callback: TMethod);
begin
  if A = 1 then
    TIntProc(Callback)(100)
  else
    TStrProc(Callback)('Hello');
end;

....

Foo(1, TMethod(IntProc));
Foo(2, TMethod(StrProc));

但如果有更好的方法,我不会推荐这种方法。尤其是因为您必须使用未经检查的强制转换,因此甚至不会受益于具有as运算符的类的运行时检查。

我个人以不同的方式解决这个问题:

public
  procedure Foo(Callback: TIntProc); overload;
  procedure Foo(Callback: TStrProc); overload;

如果您需要一个例程来处理这两个回调方法,请进行额外的私有重载:

private
  procedure Foo(IntCallback: TIntProc; StrCallback: TStrProc); overload;

像这样实施:

procedure TMyClass.Foo(IntCallback: TIntProc; StrCallback: TStrProc); 
begin
  if Assigned(IntCallback) then
    ....
  if Assigned(StrCallback) then
    ....
end;

然后实现像这样的公共重载:

procedure TMyClass.Foo(Callback: TIntProc);
begin
  Foo(Callback, nil);
end;

procedure TMyClass.Foo(Callback: TStrProc);
begin
  Foo(nil, Callback);
end;

答案 1 :(得分:2)

通过一些通用的帮助,这是可能的:

program Project14;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,TypInfo;

Type
  TMyProcedures = record
    procedure MyIntProc(I: Integer);
    procedure MyStrProc(S: String);
  end;

  TMyRec = record
    public
      class procedure CallbackTest<T>( Callback: TProc<T>); static;
  end;

class procedure TMyRec.CallbackTest<T>( Callback: TProc<T>);
begin
  case GetTypeKind(T) of
  tkInteger:
    begin
      if GetTypeData(TypeInfo(T))^.OrdType = otSLong then
        TProc<Integer>(Callback)(100);
    end;
  tkUString:
    TProc<String>(Callback)('Hello');
  end;
  else raise Exception.Create('Callback type can only be string or integer');
end;

procedure TMyProcedures.MyIntProc(I: Integer);
begin
  WriteLn(IntToStr(I));
end;

procedure TMyProcedures.MyStrProc(S: string);
begin
  WriteLn(S);
end;

var
  myProcedures: TMyProcedures;

begin
  TMyRec.CallbackTest<Integer>(myProcedures.MyIntProc);
  TMyRec.CallbackTest<String>(myProcedures.MyStrProc);
  ReadLn;
end.

通过TypeInfo调用解决了这种方法,并且类型转换修复了回调方法。

对于Delphi XE7之前的版本,您可以使用以下代码CallBackTest

class procedure TMyRec.CallbackTest<T>( Callback: TProc<T>);
begin
  if TypeInfo(T) = TypeInfo(Integer) then begin
      if GetTypeData(TypeInfo(T))^.OrdType = otSLong then
        TProc<Integer>(Callback)(100);
  end
  else if TypeInfo(T) = TypeInfo(String) then begin
    TProc<String>(Callback)('Hello');
  end
  else raise Exception.Create('Callback type can only be string or integer');
end; 

<子> 使用类型安全的方式更新,将实际的序数类型分隔为适用于Delphi 2009..XE6的整数和代码。