绕过(禁用)Delphi对接口的引用计数 [英] Bypassing (disabling) Delphi's reference counting for interfaces

查看:161
本文介绍了绕过(禁用)Delphi对接口的引用计数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于正在开发的应用程序的架构中的一个特殊问题,接口似乎是一个很好的解决方案。具体来说,一些业务对象取决于在实际应用程序中从数据库中提取的一些设置。让这些业务对象要求一个接口(通过控制反转),并让一个中央的 TDatabaseSettings 对象实现这些接口,允许更好的隔离,因此更容易进行单元测试。



然而,在Delphi中,界面似乎有一个,在这种情况下,令人不快的奖金:引用计数。这意味着如果我这样做:

 键入
IMySettings = interface
函数getMySetting:String ;
结束

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

TMyBusinessObject =类(TInterfacedObject,IMySettings)
属性设置:IMySettings读取FSettings写入FSettings;
结束

var
DatabaseSettings:TDatabaseSettings;
//全局对象(通常放置在某个控制器中)

//现在,在某些函数中...
O:= TMyBusinessObject.Create;
O.Settings:= DatabaseSettings;
// ...用O
做某事oFree;

在最后一行( O.Free ),我的全局 DatabaseSettings 对象现在也被释放,因为它的最后一个接口引用(它包含在 O )丢失!



一个解决方案是使用界面存储全局 DatabaseSettings 对象;另一个解决方案是覆盖 TDatabaseSettings 类的引用计数机制,因此我可以继续将 DatabaseSettings 一个正常的对象(这与应用程序的其他部分更加一致)。



所以,总而言之,我的问题是:如何禁用界面引用计数机制为特定类?



我已经能够找到一些建议覆盖 IInterface 方法 _AddRef _Release 为类( TDatabaseSettings

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



非常感谢!

解决方案

它,但问题是如果你真的想要这个。
如果要使用界面,最好使用它们。所以你已经体验过,如果你混合了类和接口变量,你会遇到问题。

  var 
// DatabaseSettings :TDatabaseSettings;
DatabaseSettings:IMySettings;

//现在,在某些函数中...
O:= TMyBusinessObject.Create;
O.Settings:= DatabaseSettings;
// ...用O
做某事oFree;

你现在有一个接口的第二个引用,丢失的第一个不会释放对象。 p>

也可以保持类和对象:

  var 
DatabaseSettings:TDatabaseSettings;
DatabaseSettingsInt:IMySettings;

确保在创建对象后立即设置界面。



如果你真的想禁用引用计数,你只需要创建一个实现IInterface的TObject的新的后代。我已经在D2009中测试了下面的例子,它的作用如下:

  //查询界面可以保持不变,因为它不依赖于参考计数。 
function TMyInterfacedObject.QueryInterface(const IID:TGUID; out Obj):HResult;
begin
如果GetInterface(IID,Obj)然后
结果:= 0
else
结果:= E_NOINTERFACE;
结束

构造函数TMyInterfacedObject.Create;
begin
FRefCount:= 1;
结束

procedure TMyInterfacedObject.FreeRef;
begin
如果Self = nil then
退出;
if InterlockedDecrement(FRefCount)= 0 then
Destroy;
结束

function TMyInterfacedObject._AddRef:Integer;
begin
结果:= InterlockedIncrement(FRefCount);
结束

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

FreeRef只是像_Release一样降低引用计数。您可以在通常使用Free的地方使用。


For one particular issue in the architecture of an application I'm working on, interfaces seem to be a nice solution. Specifically, some "business objects" depend on a bunch of settings that are pulled from the database in the actual app. Letting those business objects ask for an interface (through Inversion of Control), and letting a central TDatabaseSettings object implement those interfaces, allows for better isolation, and thus for much easier unit testing.

However, in Delphi, interfaces seem to come with an, in this case, unpleasant bonus: reference counting. This means that if I do something like this:

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;

On the last line (O.Free), my global DatabaseSettings object is now also freed, since the last interface reference to it (which was contained in O) is lost!

One solution would be to store the 'global' DatabaseSettings object with an interface; another solution would be to override the reference counting mechanism for the TDatabaseSettings class, so I can continue to manage the DatabaseSettings as a normal object (which is much more consistent with the rest of the app).

So, in summary, my question is: how do I disable the interface reference counting mechanism for a particular class?

I've been able to find some info that suggests overriding the IInterface methods _AddRef and _Release for the class (TDatabaseSettings in the example); has anyone ever done that?

Or would you say I shouldn't do this (confusing? just a bad idea?), and find a different solution to the architectural problem?

Thanks a lot!

解决方案

Ok, you can bypass it, but the question is if you really want that. If you want to use interfaces, you better use them completely. So as you have experienced it, you get problems if you mix class and interface variables.

var
  // DatabaseSettings: TDatabaseSettings; 
  DatabaseSettings : IMySettings;

//Now, in some function...
O := TMyBusinessObject.Create;
O.Settings := DatabaseSettings; 
// ... do something with O
O.Free;

You now have a second reference to the interface and losing the first will not free the object.

It as also possible to keep both the class and the object:

var
  DatabaseSettings: TDatabaseSettings; 
  DatabaseSettingsInt : IMySettings;

Be sure to set the interface right after the object has been created.

If you really want to disable reference counting, you just have to create a new descendant of TObject that implements IInterface. I have tested the example below in D2009 and it works:

// 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 just lowers the refcount just like _Release. You can use it where you normally use Free.

这篇关于绕过(禁用)Delphi对接口的引用计数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆