Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
绕过(禁用)Delphi';s接口的引用计数_Delphi - Fatal编程技术网

绕过(禁用)Delphi';s接口的引用计数

绕过(禁用)Delphi';s接口的引用计数,delphi,Delphi,对于我正在处理的应用程序体系结构中的一个特定问题,接口似乎是一个很好的解决方案。具体来说,一些“业务对象”依赖于从实际应用程序中的数据库中提取的一组设置。让这些业务对象请求接口(通过控制反转),并让一个中央TDatabaseSettings对象实现这些接口,可以实现更好的隔离,从而更容易进行单元测试 然而,在Delphi中,接口似乎附带了一个令人不快的好处:引用计数。这意味着如果我这样做: type IMySettings = interface function getMySettin

对于我正在处理的应用程序体系结构中的一个特定问题,接口似乎是一个很好的解决方案。具体来说,一些“业务对象”依赖于从实际应用程序中的数据库中提取的一组设置。让这些业务对象请求接口(通过控制反转),并让一个中央
TDatabaseSettings
对象实现这些接口,可以实现更好的隔离,从而更容易进行单元测试

然而,在Delphi中,接口似乎附带了一个令人不快的好处:引用计数。这意味着如果我这样做:

type
IMySettings = interface
    function getMySetting: String;
end;

TDatabaseSettings = class(..., IMySettings)
    //...
end;

TMyBusinessObject = class(TInterfacedObject, IMySettings)
    property Settings: IMySettings read FSettings write FSettings;
end;

var
  DatabaseSettings: TDatabaseSettings; 
    // global object (normally placed in a controller somewhere)

//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings; 
// ... do something with O
O.Free;
在最后一行(
O.Free
),我的全局
DatabaseSettings
对象现在也被释放,因为对它的最后一个接口引用(包含在
O
中)丢失了

一种解决方案是使用接口存储“全局”数据库设置对象;另一个解决方案是覆盖
TDatabaseSettings
类的引用计数机制,这样我可以继续将
DatabaseSettings
作为普通对象进行管理(这与应用程序的其余部分更加一致)

总之,我的问题是:如何禁用特定类的接口引用计数机制?

我已经找到一些信息,建议覆盖类的
接口
方法
\u AddRef
\u Release
TDatabaseSettings
);有人这样做过吗

或者你会说我不应该这样做(混淆?只是一个坏主意?),并找到一个不同的解决方案来解决架构问题


非常感谢

\u AddRef
\u Release
\u QueryInterface
实际上是您想要覆盖的内容。但是,您应该非常清楚自己在做什么,因为这可能会导致内存泄漏或奇怪的、难以发现的bug


不要从
TInterfacedObject
开始,而是从
TObject
开始,实现返回1的前两个方法的您自己的版本。

好的,您可以绕过它,但问题是您是否真的想要它。 如果您想使用接口,最好完全使用它们。所以,正如您所经历的,如果混合使用类和接口变量,您会遇到问题

var
  // DatabaseSettings: TDatabaseSettings; 
  DatabaseSettings : IMySettings;

//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings; 
// ... do something with O
O.Free;
现在您有了对接口的第二个引用,丢失第一个引用不会释放对象

还可以尽可能保留类和对象:

var
  DatabaseSettings: TDatabaseSettings; 
  DatabaseSettingsInt : IMySettings;
确保在创建对象后立即设置接口

如果您真的想禁用引用计数,您只需要创建一个实现IInterface的ToObject的新子体。我已经在D2009中测试了下面的示例,它是有效的:

// Query Interface can stay the same because it does not depend on reference counting.
function TMyInterfacedObject.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
  if GetInterface(IID, Obj) then
    Result := 0
  else
    Result := E_NOINTERFACE;
end;

constructor TMyInterfacedObject.Create;
begin
  FRefCount := 1;
end;

procedure TMyInterfacedObject.FreeRef;
begin
  if Self = nil then
    Exit;
  if InterlockedDecrement(FRefCount) = 0 then
    Destroy;    
end;

function TMyInterfacedObject._AddRef: Integer;
begin
  Result := InterlockedIncrement(FRefCount);
end;

function TMyInterfacedObject._Release: Integer;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then
    Destroy;
end;

FreeRef只是降低refcount,就像释放一样。您可以在正常情况下免费使用它。

禁用此类问题的引用计数闻起来很糟糕。 一个更好的体系结构解决方案是使用某种“单例”模式。 实现这一点的最简单方法如下所示:

interface 

type

TDatabaseSettings = class(..., IMySettings)
end;

function DatabaseSettings: IMySettings;

implementation

var
  GDatabaseSettings: IMySettings; 

function DatabaseSettings: IMySettings;
begin
 if GDatabaseSettings = nil then GDatabaseSettings := TDatabaseSettings.Create;
 Result := GDatabaseSettings;
end;

O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings; 
O.Free;
顺便说一下:使用接口时:始终使用接口变量!不要混合使用两个class en接口变量(使用“var设置:IMySettings”而不是“var设置:TDatabaseSettings”)。否则引用计数将妨碍您(自动销毁、无效指针操作等)。
在上面的解决方案中,GDatabaseSettings也是“IMySettings”类型,因此它会获得一个正确的引用计数,并将持续到程序终止。

要禁用引用计数,AddRef和Release只能返回-1

function TMyInterfacedObject._AddRef: Integer;
begin
  Result := -1;
end;

function TMyInterfacedObject._Release: Integer;
begin
  Result := -1;
end;

在没有引用计数的接口中有很多实用程序。如果使用引用计数,则不能混合对象引用和接口引用,因为会发生不好的事情。通过禁用引用计数,您可以愉快地混合接口和对象引用,而无需担心对象突然自动销毁。

或者只需使用以下代码:

var I: IMyInterface; begin I := ...; ... Do whatever you want in a scope; Initialize(I); //- this will clear the interface variable without calling the _release. end. 变量 I:IMyInterface; 开始 I:=。。。; ... 在一个范围内做任何你想做的事; 初始化(I);//-这将在不调用_release的情况下清除接口变量。 结束。 不要从标准装置下降,而是从标准装置下降

  • TSingletonImplementation是需要基本接口实现的简单类的基础,引用计数被禁用
  • TSingletonImplementation是支持接口的Delphi类的线程安全基类。与TInterfacedObject不同,TSingletonImplementation不实现引用计数

真是太快了。。。非常感谢!你认为我使用接口的方式有意义吗?让我感到奇怪的是,整个引用计数都出现了,真的——为什么不把它们作为解耦类的好方法呢?引用计数实际上非常灵活。您不需要释放对象;当分配给它的变量不在范围内时,Delphi会帮你做的。是的,这是真的,它很光滑(尽管我还没有找到它的用途)。但是能够禁用它也很好,因为如果与“传统”对象管理混合使用,它确实会使事情变得不一致:)返回-1只是一种约定,返回值本身并不重要,只要它不是0(这将导致实现对象的破坏)。@Mghie:实际上,因为调用Destroy(即在TInterfacedObject上)的是_Release方法本身,而不是一些外部代码,所以您甚至不必担心返回0。不过,最好返回-1,让最终用户知道这不是一个参考计数界面,以防他们在意。非常感谢您的广泛回复,非常感谢!是的,在我离开之前我应该多想想