Delphi 我可以使用接口而不调用隐藏的try finally';s

Delphi 我可以使用接口而不调用隐藏的try finally';s,delphi,interface,refcounting,Delphi,Interface,Refcounting,我想使用接口来处理不同的类型,而不是笨重的类型 t它现在使用的方向 显示70%以上的运行时间花费在字典中 因此,我将制作如下界面: TSyntaxNode = class ... end; IIdentifier = interface ['{D72E945D-E397-4E02-B702-8AE6C235F9C6}'] function GetIdentifier: string; property Identifier: string read GetIdentifier;

我想使用接口来处理不同的类型,而不是笨重的类型 t它现在使用的方向

显示70%以上的运行时间花费在字典中

因此,我将制作如下界面:

TSyntaxNode = class
  ...
end;

IIdentifier = interface
  ['{D72E945D-E397-4E02-B702-8AE6C235F9C6}']
  function GetIdentifier: string;
  property Identifier: string read GetIdentifier;
end;

IMethod = interface(Identifier)
  ['{8E6119DC-E0F3-42BD-A5BF-FB658E34499E}']
  .....
end;

TMethodNode = class(TSyntaxNode, IMethod, IIdentifier,...)
 ...
end;
罗曼认为问题在于:

引用计数可能会导致性能问题。DelphiAST创建数千个类来生成语法树(当输入文件足够大时,超过100000个TSyntaxNode实例)。引用计数器将被调用多少次

每次发生这种情况时,都会调用一个隐藏的
try finally
,这将大大降低速度

在方法参数中严格使用
const
可以防止refcount代码调用该方法,但如果每次执行类似于
MyRef=List[0]
的操作时都会发生这种情况,即使该项仍然存在于列表中,它也会增加分配给
MyRef
的refcount

如何在不必担心重新计数和最后尝试块的情况下使用接口?
我非常乐意手动管理类的销毁

进一步信息
我猜我需要使用
TAggregatedObject
作为基本祖先。
我在某个地方读到,不分配GUID会禁止引用计数,但必须使用源代码来备份引用计数。
然而,丢失GUID将导致在获取子接口时出现问题,因此我必须设计解决方案

我可以在不调用隐藏的try finally的情况下使用接口吗

不可以。不管发生什么,编译器都会通过接口发出引用计数代码。你无法避免

您可以使用函数指针的记录来实现自己版本的接口。它将更加笨重,但将避免堆分配和引用计数

“成千上万的物体”总是让我不寒而栗。内存中的对象有很大的开销。您忘记了它,但当您试图管理数千个时,或者注意到它的性能下降,或者开始尝试从文件中写入或读取时,它会再次弹出

据我所知,使用接口不会有太大变化,因为您仍然在下面使用对象(类实例)

这种规模的努力需要特定地使用良好的旧的直接到内存的数据结构。例如,我一直在玩一个存储在记录数组中的AST:

是否如果不调用
try finally
和refcounting,您就不能使用接口。
但是,您可以大大减少隐藏异常处理程序的数量。
你只需要非常小心地做两件事

  • 传递接口时始终使用
    const
    参数

  • 永远不要将接口存储在接口类型变量中,而是使用自制记录封装接口,以便不会触及其refcount

  • 以下是封装记录的示例:

    type
      TInterface<Intf: IInterface> = record
      private
        P: Pointer;
      public
        function I: Intf; inline;
        class operator Implicit(const A: Intf): TInterface<Intf>; inline;
      end;
    
    function TInterface<Intf>.I: Intf;
    begin
      pointer(IInterface(Result)):= P;
    end;
    
    class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
    begin
      Result.P:= pointer(IInterface(A));
    end;
    
    类型
    TInterface=记录
    私有的
    P:指针;
    公众的
    功能一:Intf;内联;
    类运算符隐式(常量A:Intf):TInterface;内联;
    结束;
    函数TInterface.I:Intf;
    开始
    指针(界面(结果)):=P;
    结束;
    类运算符TInterface.Implicit(常量A:Intf):TInterface;
    开始
    结果.P:=指针(界面(A));
    结束;
    
    这里有一个示例程序来演示这个概念

    program Project32;
    {$APPTYPE CONSOLE}
    
    {$R *.res}
    uses
      System.SysUtils;
    
    type
      TInterface<Intf: IInterface> = record
      private
        P: Pointer;
      public
        function I: Intf; inline;
        class operator Implicit(const A: Intf): TInterface<Intf>; inline;
      end;
    
      ITest1 = interface
        function Test1: integer;
      end;
    
      ITest2 = interface
        function Test2: integer;
      end;
    
      TTest = class(TAggregatedObject, ITest1, ITest2)
        function Test1: integer;
        function Test2: integer;
      end;
    
    { TTest }
    
    function TTest.Test1: integer;
    begin
      Result:= 1;
    end;
    
    function TTest.Test2: integer;
    begin
      Result:= 2;
    end;
    
    { TInterface<Intf> }
    
    function TInterface<Intf>.I: Intf;
    begin
      pointer(IInterface(Result)):= P;
    end;
    
    class operator TInterface<Intf>.Implicit(const A: Intf): TInterface<Intf>;
    begin
      Result.P:= pointer(IInterface(A));
    end;
    
    var
      I1: TInterface<ITest1>;
      I2: TInterface<ITest2>;
      Test: TTest;
    
    begin
      Test:= TTest.Create(nil);  //Force AV on _AddRef, _Release
      If (Test.Test1 = 1) then WriteLn(S);
      I1:= Test;
      If (I1.I.Test1 =1) then WriteLn(S);
      I2:= Test;
      If (I2.I.Test2 = 2) then WriteLn(S);
      ReadLn(s);
      Test.Free;
    end.
    
    程序项目32;
    {$APPTYPE控制台}
    {$R*.res}
    使用
    System.SysUtils;
    类型
    TInterface=记录
    私有的
    P:指针;
    公众的
    功能一:Intf;内联;
    类运算符隐式(常量A:Intf):TInterface;内联;
    结束;
    ITest1=接口
    函数Test1:整数;
    结束;
    ITest2=接口
    函数Test2:整数;
    结束;
    TTest=class(TAggregatedObject、ITest1、ITest2)
    函数Test1:整数;
    函数Test2:整数;
    结束;
    {TTest}
    函数TTest.Test1:整数;
    开始
    结果:=1;
    结束;
    函数TTest.Test2:整数;
    开始
    结果:=2;
    结束;
    {TInterface}
    函数TInterface.I:Intf;
    开始
    指针(界面(结果)):=P;
    结束;
    类运算符TInterface.Implicit(常量A:Intf):TInterface;
    开始
    结果.P:=指针(界面(A));
    结束;
    变量
    I1:着色面;
    I2:着色面;
    试验:TTest;
    开始
    测试:=测试创建(无)//强制AV打开_AddRef,_释放
    如果(Test.Test1=1),则写入n;
    I1:=试验;
    如果(I1.I.Test1=1),则写入n(S);
    I2:=试验;
    如果(I2.I.Test2=2),则写入n(S);
    ReadLn(s);
    免费测试;
    结束。
    
    TAggregatedObject
    没有处理
    \u AddRef
    /
    \u Release
    调用的接口。
    在程序的生命周期内,不会出现任何问题,但是Delphi会将
    TTest
    的创建包装在try finally中,这将在退出函数时生成异常


    在实际使用中,您必须使用TInterfacedObject。不过,如果您经常传递接口引用,可能会有所帮助

    是的,这会起作用,我记得在一个delphi博客中看到过类似的东西,但不记得在哪里。只需声明一个包含函数指针成员的记录。手动实现的接口。这里有一些关于如何使用记录实现接口的信息:但我希望接口的接口部分消失,所以try最终消失。将
    MyRef
    声明为
    指针。赋值语句将起作用。当需要访问接口的方法时,键入cast将其转换为所需的接口类型。类型转换不会导致引用计数。您正在向错误的方向优化。在DelphiAST中优化代码的方法是使用多态性,因为这就是它的用途。为什么DelphiAST甚至使用泛型?@SilverWarior,泛型不会导致缓慢,可能是代码膨胀,但不会导致缓慢。这不是问题的真正答案,但我对您的解决方案很好奇。顺便说一句,德尔夫的缓慢