o.s*_*wab 8 delphi delegates function-pointers rtti
我已经定义了以下内容:
方法指针,如果验证正常或错误代码,则返回0
TValidationFunc = Function(AParam: TAnObject): integer Of Object;
Run Code Online (Sandbox Code Playgroud)要执行的函数列表:
Functions: TObjectList < TValidationFunc>;
Run Code Online (Sandbox Code Playgroud)我在函数列表中放了几个带有此签名的函数.
要执行它们,我执行:
For valid In Functions Do
Begin
res := -1;
Try
res := valid(MyObject);
Except
On E: Exception Do
Log('Error in function ??? : ' + E.Message, TNiveauLog.Error, 'PHVL');
End;
Result := Result And (res = 0);
End;
Run Code Online (Sandbox Code Playgroud)
如果此函数引发异常,我如何在日志中获取原始函数的名称?
好吧,永远不要说永远:-)。此函数将返回作为参数传递的事件的方法名称(采用 <ClassType>.<MethodName> 形式,即 TMainForm.FormCreate)。不幸的是,您不能使用无类型参数来允许传入任何类型的事件,但必须为您希望能够“解码”的每个方法签名编写特定的例程:
FUNCTION MethodName(Event : TValidationFunc) : STRING;
VAR
M : TMethod ABSOLUTE Event;
O : TObject;
CTX : TRttiContext;
TYP : TRttiType;
RTM : TRttiMethod;
OK : BOOLEAN;
BEGIN
O:=M.Data;
TRY
OK:=O IS TObject;
Result:=O.ClassName
EXCEPT
OK:=FALSE
END;
IF OK THEN BEGIN
CTX:=TRttiContext.Create;
TRY
TYP:=CTX.GetType(O.ClassType);
FOR RTM IN TYP.GetMethods DO
IF RTM.CodeAddress=M.Code THEN
EXIT(O.ClassName+'.'+RTM.Name)
FINALLY
CTX.Free
END
END;
Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
END;
Run Code Online (Sandbox Code Playgroud)
像这样使用它:
For valid In Functions Doc Begin
res := -1;
Try
res := valid(MyObject);
Except
On E: Exception Do
Log('Error in function '+MethodName(valid)+' : ' + E.Message, TNiveauLog.Error, 'PHVL');
End;
Result := Result And (res = 0);
End;
Run Code Online (Sandbox Code Playgroud)
我没有用上面的代码尝试过,但已经用我的 MainForm 的 FormCreate 尝试过。
有一点需要注意:这仅适用于生成 RTTI 的方法,并且仅适用于 Delphi 2010 及更高版本(其中它们大大增加了 RTTI 可用的数据量)。因此,为了确保它有效,您应该将要跟踪的方法放在 PUBLISHED 部分中,因为这些方法总是(默认情况下)会生成 RTTI。
如果你想让它更通用一点,你可以使用这个结构:
FUNCTION MethodName(CONST M : TMethod) : STRING; OVERLOAD;
VAR
O : TObject;
CTX : TRttiContext;
TYP : TRttiType;
RTM : TRttiMethod;
OK : BOOLEAN;
BEGIN
O:=M.Data;
TRY
OK:=O IS TObject;
Result:=O.ClassName
EXCEPT
OK:=FALSE
END;
IF OK THEN BEGIN
CTX:=TRttiContext.Create;
TRY
TYP:=CTX.GetType(O.ClassType);
FOR RTM IN TYP.GetMethods DO
IF RTM.CodeAddress=M.Code THEN
EXIT(O.ClassName+'.'+RTM.Name)
FINALLY
CTX.Free
END
END;
Result:=IntToHex(NativeInt(M.Code),SizeOf(NativeInt)*2)
END;
FUNCTION MethodName(Event : TValidationFunc) : STRING; OVERLOAD; INLINE;
BEGIN
Result:=MethodName(TMethod(Event))
END;
Run Code Online (Sandbox Code Playgroud)
然后,您只需要为每个简单调用通用实现的事件编写一个特定的 MethodName,如果您将其标记为 INLINE,则很有可能它甚至不会产生额外的函数调用,而是直接调用它。
顺便说一句:我的回答很大程度上受到 Cosmin Prund 一年前在这个问题中给出的代码的影响:RTTI information for method pointer
如果您的 Delphi 没有定义 NativeInt (不记得他们具体何时实现它),只需将其定义为:
{$IFNDEF CPUX64 }
TYPE
NativeInt = INTEGER;
{$ENDIF }
Run Code Online (Sandbox Code Playgroud)