是否存在像TInterfacedObject一样的非引用计数基类?

17

我需要一个基类,类似于TInterfacedObject,但不包含引用计数(类似于TNonRefCountedInterfacedObject)。

实际上这已经是我第n次需要这样一个类了,但我总是不断地重新编写我的代码(即复制并粘贴)。我无法相信没有官方的基类可供使用。

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


我知道 TComponent 会禁用引用计数,但你可能不想携带 TComponent 中的所有东西。我不知道是否有任何预定义的类,我写了自己的类。 - jpfollenius
@Smasher:我曾经想过使用TComponent,但还是决定先问一下问题;) 我想使用一个小的、专用的类。 - Heinrich Ulbricht
感谢您的所有输入,考虑到您不同的实现方式真是太有趣了!我喜欢为调试添加名称,但TInterfacedPersistent对我来说仍然太重了,我差点使用TPureInterfacedObject(我喜欢这个名字!),但是@Erwin介绍了纯本地实现!尽管我不喜欢TSingletonImplementation这个名字。 - Heinrich Ulbricht
2
对于完美主义者来说,这个名字真的很烦人。我会创建一个类型别名TNonRefCountedInterfacedObject = TSingletonImplementation,以免混淆自己。 - stanleyxu2005
6个回答

18

在单位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;

4
您可以考虑使用 TInterfacedPersistent 。如果您没有重写 GetOwner 函数,它将不进行引用计数。

3

我做了这个。 它可以代替TInterfacedObject,带或不带引用计数。 它还有一个名字属性——在调试时非常有用。

// 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;


// =============================================================================

1
我本想因为你没有使用预定义的 E_NOINTERFACE 而责备你。但我刚刚注意到 VCL 定义了它四次,所以你只是一个轻微的罪犯。:-P - Uli Gerhardt
QueryInterface 中的行为切换有什么意义?在我看来似乎没必要。 - David Heffernan

1

我不知道有任何现成的基类,所以我自己写了一个(就像你一样)。只需将其放入公共 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;

2
为什么要以如此受限的方式实现 QueryInterface? - David Heffernan
我没有其他实现的用途。但是为了完整起见,也许我会从TInterfacedPersistent.QueryInterface中复制实现。 - Uli Gerhardt

1

虽然没有这样的类,但是你可以像其他人一样轻松地编写自己的类。然而,我想知道为什么你需要它。在我的经验中,即使你想混合对象和接口引用,也很少真正需要这样的类。

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

换句话说,我建议不要使用这样的类。


1
非常感谢您的警告,风险确实存在。我通常也主张在同一对象上使用对象或接口中的一个,而不是两者都用。但有些情况下,这样做可以解决某些问题... - Heinrich Ulbricht
可以同时使用两者,没有问题。只需要注意引用计数的问题,即不要释放接口对象。 - Rudy Velthuis

0
自 Delphi 11 起,Embarcadero 在 System 单元中添加了 TNoRefCountObject。以下是发布说明中的注释:
新类 System.TNoRefCountObject 是一个非引用计数 IInterface 实现(替换了旧的、奇怪命名的 TSingletonObject)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接