【问题标题】:Is there a non-reference-counted base class like TInterfacedObject?是否有像 TInterfacedObject 这样的非引用计数基类?
【发布时间】:2011-10-28 03:41:56
【问题描述】:

我需要一个像TInterfacedObject 这样的基类,但没有引用计数(所以是一种TNonRefCountedInterfacedObject)。

这实际上是我第 n 次需要这样的课程,不知何故,我总是一次又一次地编写(阅读:复制和粘贴)我自己的课程。我不敢相信没有我可以使用的“官方”基类。

在实现IInterface 但没有引用计数的 RTL 中是否有一个基类,我可以从中派生我的类?

【问题讨论】:

  • 我知道TComponent 确实禁用了引用计数,但您可能不想携带TComponent 中的所有内容。我不知道任何预定义的类,我自己写了一个。
  • @Smasher:我很想使用TComponent,但后来决定先问这个问题;)我想使用一个小型的专用课程。
  • 感谢您的所有意见,考虑到您所有不同的实现,这很有趣!我喜欢添加一个用于调试的名称,TInterfacedPersistent 对我的口味来说仍然太重了,我几乎使用了TPureInterfacedObject(我喜欢这个名字!)但随后@Erwin 介入并向我展示了纯原生实现!虽然我不喜欢TSingletonImplementation这个名字。
  • 对于完美主义来说,这个名字真的很讨厌。我会创建一个类型别名TNonRefCountedInterfacedObject = TSingletonImplementation,以免自己混淆。

标签: delphi interface delphi-xe reference-counting


【解决方案1】:

在单元Generics.Defaults 中定义了一个类TSingletonImplementation。在 Delphi 2009 及更高版本中可用。

  // A non-reference-counted IInterface implementation.
  TSingletonImplementation = class(TObject, IInterface)
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;

【讨论】:

    【解决方案2】:

    我做到了。它可以代替 TInterfacedObject 使用或不使用引用计数。它还有一个 name 属性——在调试时非常有用。

    // TArtInterfacedObject
    // =============================================================================
    
    
    // An object that supports interfaces, allowing naming and optional reference counting
    type
      TArtInterfacedObject = class( TInterfacedObject )
        constructor Create( AReferenceCounted : boolean = True);
      PRIVATE
        FName             : string;
        FReferenceCounted : boolean;
      PROTECTED
        procedure SetName( const AName : string ); virtual;
      PUBLIC
    
        property Name : string
                   read FName
                   write SetName;
    
        function QueryInterface(const AGUID : TGUID; out Obj): HResult; stdcall;
        function SupportsInterface( const AGUID : TGUID ) : boolean;
        function _AddRef: Integer; stdcall;
        function _Release: Integer; stdcall;
    
      end;
    
    // =============================================================================
    
    
    
    
    { TArtInterfacedObject }
    
    constructor TArtInterfacedObject.Create( AReferenceCounted : boolean = True);
    begin
      inherited Create;
    
      FName := '';
    
      FReferenceCounted := AReferenceCounted;
    end;
    
    function TArtInterfacedObject.QueryInterface(const AGUID: TGUID; out Obj): HResult;
    const
      E_NOINTERFACE = HResult($80004002);
    begin
      If FReferenceCounted then
        Result := inherited QueryInterface( AGUID, Obj )
       else
        if GetInterface(AGUID, Obj) then Result := 0 else Result := E_NOINTERFACE;
    end;
    
    
    procedure TArtInterfacedObject.SetName(const AName: string);
    begin
      FName := AName;
    end;
    
    function TArtInterfacedObject.SupportsInterface(
      const AGUID: TGUID): boolean;
    var
      P : TObject;
    begin
      Result := QueryInterface( AGUID, P ) = S_OK;
    end;
    
    
    function TArtInterfacedObject._AddRef: Integer;
    begin
      If FReferenceCounted then
        Result := inherited _AddRef
       else
        Result := -1   // -1 indicates no reference counting is taking place
    end;
    
    function TArtInterfacedObject._Release: Integer;
    begin
      If FReferenceCounted then
        Result := inherited _Release
       else
        Result := -1   // -1 indicates no reference counting is taking place
    end;
    
    
    // =============================================================================
    

    【讨论】:

    • 我想责备你没有使用预定义的E_NOINTERFACE。但我只是注意到 VCL 定义了它 四次 次,所以你是一个轻微的罪犯。 :-P
    • QueryInterface 中行为切换的意义何在?对我来说似乎没有必要。
    【解决方案3】:

    您可以考虑TInterfacedPersistent。如果您不覆盖 GetOwner,它不会进行引用计数。

    【讨论】:

      【解决方案4】:

      我不知道任何开箱即用的基类,所以我编写了自己的(和你一样)。只需将它放在一个通用的 utils 单元中就可以了。

      type
        TPureInterfacedObject = class(TObject, IInterface)
        protected
          { IInterface }
          function QueryInterface(const IID: TGUID; out Obj): HResult; virtual; stdcall;
          function _AddRef: Integer; stdcall;
          function _Release: Integer; stdcall;
        end;
      
      { TPureInterfacedObject }
      
      function TPureInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
      begin
        Result := E_NOINTERFACE;
      end;
      
      function TPureInterfacedObject._AddRef: Integer;
      begin
        Result := -1;
      end;
      
      function TPureInterfacedObject._Release: Integer;
      begin
        Result := -1;
      end;
      

      【讨论】:

      • 为什么要以这种限制方式实现QueryInterface。
      • 否则我没有用它来实现它。但为了完整起见,也许我会从 TInterfacedPersistent.QueryInterface 复制实现。
      【解决方案5】:

      没有这样的课程,但您可以轻松编写自己的课程,正如其他人所展示的那样。但是,我确实想知道您为什么需要它。根据我的经验,即使您想混合对象和接口引用,也很少真正需要这样的类。

      还请注意,当您使用这样的类时,您仍然需要注意在它们离开作用域和释放对象之前将您对此类对象的任何接口引用设置为 nil。否则,您可能会遇到运行时尝试在已释放对象上调用 _Release 的情况,这往往会导致无效指针异常。

      IOW,我建议反对完全使用这样的课程。

      【讨论】:

      • 非常感谢您的警告,风险肯定存在。我通常也提倡使用对象或接口,但不能同时在同一个对象上使用。但是在某些情况下它可以解决某些问题......
      • 一个可以同时使用,没问题。必须只知道引用计数问题,即不得释放接口对象。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-17
      • 2010-10-05
      相关资源
      最近更新 更多