Delphi中的元编程 - 为类的每个方法添加函数调用

时间:2014-02-17 19:40:25

标签: delphi metaprogramming aop

我想在应用程序中添加一些工具,基本上我想添加一个函数调用作为许多数据模块中每个方法/事件的第一行,代码大致如下。

procedure TSomeClass.SomeProcedure;   
begin
  ExecutionCounter(ClassName, 'SomeProcedure'); //This is what I want to insert.
  //the rest of the procedure
end;

我可以手动完成数以千计的方法(可能需要几个小时),但我想知道是否有一些编程方式可以做到这一点。

我可以使用正则表达式轻松删除代码,但无法想出添加它的方法(确定beginprocedure之后的第一个function并插入具有适当参数的代码)。

这对于Delphi中的AOP来说是一个很好的例子,但它得不到很好的支持。

对于某些背景,我正在使用大型遗留应用程序服务器(DataSnap / DCOM),并且我想确定客户端仍在调用哪些函数。 ExecutionCounter函数返回一个类作为接口,在类析构函数中它记录类名,方法名,增加该方法的执行次数和总运行时间(当然是在一个单独的线程中)。

2 个答案:

答案 0 :(得分:3)

DSharp(https://bitbucket.org/sglienke/dsharp)有一些方面。所有方法都需要是虚拟的,这是一个非常真实的限制,但这可能是你可以使用的。

以下是他的AOP示例的链接:

https://bitbucket.org/sglienke/dsharp/src/ad7c5983505f0117f1347f92d2bb96c07bdfda94/Samples/AOP/?at=master

对于你的例子,你会做这样的事情:

TMyAspect = class(TAspect)
public
  class procedure DoBefore(Instance: TObject; Method: TRttiMethod;
    const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue); override;
end;

实现可能看起来像这样:

class procedure TMyAspect.DoBefore(Instance: TObject; Method: TRttiMethod;
  const Args: TArray<TValue>; out DoInvoke: Boolean; out Result: TValue);
begin
  inherited;
  ExecutionCounter(Instance.ClassName, Method.Name);
end;

你会把它挂在像这样的方法上:

AspectWeaver.AddAspect(TSomeClass, TMyAspect, 'SomeProcedure');

答案 1 :(得分:0)

最后我手动做了一些。有647个方法我添加了函数调用。我的解决方案是在CnPack中使用脚本,我编写了以下脚本

program InsertInstrumentation;

{
  Note: Please Add this Script to Script Library, and Run it from the
    Corresponding item of the Dropdown Menu under "Run" ToolButton in
    Script Window. Or Assign a Shortcut to Run it.
}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
var
  EditView: IOTAEditView;
  Proc : string;
  dotpos : integer;
begin
  EditView := CnOtaGetTopMostEditView(nil);
  if EditView <> nil then
    EditView.GetPosition.MoveBOL; // Move the cursor to the beginning of the line
  proc := CnOtaGetCurrentProcedure;
  dotPos := pos('.', proc);
  if dotPos > 0 then //remove the class name if it is present
    Delete(Proc, 1, dotPos);
  IdeInsertTextIntoEditor('  ExecutionCounter(ClassName, ''' + Proc + ''');' + #13#10);
end.

它在当前光标位置添加了ExecutionCounter(ClassName, 'TheCurrentMethodName');作为新行。我将它分配给了CTRL + SHIFT + Q并在我的应用程序中完成了工作。有点手动,但花了一个多小时才做。本来希望有一些机制可以自动完成 - 我想我应该学习Ruby或Perl或其他一些脚本语言。