绕过(禁用)Delphi对接口的引用计数 [英] Bypassing (disabling) Delphi's reference counting for interfaces
问题描述
对于正在开发的应用程序的架构中的一个特殊问题,接口似乎是一个很好的解决方案。具体来说,一些业务对象取决于在实际应用程序中从数据库中提取的一些设置。让这些业务对象要求一个接口(通过控制反转),并让一个中央的 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屋!