我有一个对象,它将一个特别复杂的接口的实现委托给子对象。这个 确切地说 我认为是工作 TAggregatedObject
。 “儿童“对象保持对它的弱引用”调节器“, 和所有 QueryInterface
请求将传递回父级。这保持了规则 IUnknown
永远是同一个对象。
所以,我的父母(即 “控制器”)object声明它实现了 IStream
接口:
type
TRobot = class(TInterfacedObject, IStream)
private
function GetStream: IStream;
public
property Stream: IStream read GetStrem implements IStream;
end;
注意: 这是一个假设的例子。我选择了这个词 Robot
因为它听起来很复杂,而且
这个词只有5个字母 - 它就是
短。我也选择了 IStream
因为
它的简短。我打算用
IPersistFile
要么 IPersistFileInit
,
但他们的时间更长了,并且做得更好
示例代码更难实现。其他
单词:这是一个假设的例子。
现在我有我的子对象将实现 IStream
:
type
TRobotStream = class(TAggregatedObject, IStream)
public
...
end;
所有剩下的,这就是我的问题开始的地方:创建 RobotStream
什么时候被要求:
function TRobot.GetStream: IStream;
begin
Result := TRobotStream.Create(Self) as IStream;
end;
此代码无法编译,错误 Operator not applicable to this operand type.
。
这是因为德尔福正试图执行 as IStream
在一个没有实现的对象上 IUnknown
:
TAggregatedObject = class
...
{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
...
IUnknown 方法 可能在那里,但对象没有 广告 它支持 IUnknown
。没有 IUnknown
界面,Delphi无法调用 QueryInterface
执行演员表演。
所以我改变了 TRobotStream
class来宣传它实现了缺少的接口(它做了;它从它的祖先继承它):
type
TRobotStream = class(TAggregatedObject, IUnknown, IStream)
...
现在它编译,但在运行时崩溃:
Result := TRobotStream.Create(Self) as IStream;
现在我可以看到 什么是 发生了,但我无法解释 为什么。德尔福正在打电话 IntfClear
,在我的父母身上 Robot
对象,在从子对象的构造函数出来的路上。
我不知道防止这种情况的正确方法。我可以尝试强迫演员:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
并希望保留参考。事实证明它确实保留了引用 - 在构造函数的出路上没有崩溃。
注意: 这对我来说很困惑。因为我过世了 目的 哪里
一个 接口 是期待。我会
假设编译器是隐式的
预成型,即:
Result := TRobotStream.Create(Self
就像IUnknown);
为了满足这个要求。该
事实上,语法检查器没有
抱怨让我假设一切都是
正确。
但崩溃还没有结束。我把线改为:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
并且代码确实从构造函数返回 TRobotStream
没有破坏我的父对象,但现在我得到一个堆栈溢出。
原因是 TAggregatedObject
推迟一切 QueryInterface
(即类型转换)返回父对象。就我而言,我正在施展 TRobotStream
到了 IStream
。
当我问的时候 TRobotStream
为了它 IStream
在......的最后:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
它转过身来问它 调节器 为了 IStream
interface,触发对以下内容的调用:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
转过来并打电话:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
繁荣! 堆栈溢出。
盲目地,我尝试删除最终的演员阵容 IStream
,让Delphi试图将对象隐含地转换为一个接口(我刚才看到的不正常):
Result := TRobotStream.Create(Self as IUnknown);
现在没有崩溃;我不太了解这一点。我构建了一个对象,一个支持多个接口的对象。现在,Delphi知道如何构建界面?它是否正在执行正确的引用计数?我在上面看到它没有。是否有一个微妙的错误等待客户崩溃?
所以我有四种可能的方式来调用我的一行。哪一项有效?
Result := TRobotStream.Create(Self);
Result := TRobotStream.Create(Self as IUnknown);
Result := TRobotStream.Create(Self) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
真正的问题
我遇到了一些微妙的错误,并且难以理解编译器的复杂性。这让我相信我做的一切都完全错了。如果需要,忽略我说的一切,并帮我回答这个问题:
将接口实现委托给子对象的正确方法是什么?
也许我应该使用 TContainedObject
代替 TAggregatedObject
。也许两者协同工作,父母应该在那里工作 TAggregatedObject
而孩子是 TContainedObject
。也许这是相反的方式。也许 也不 适用于这种情况。
注意: 我帖子主要部分的所有内容都可以忽略。这只是
表明我已经考虑过了。
有人会争辩说
通过包括我尝试过的东西,我有
毒害了可能的答案;宁
人们,回答我的问题
可能会关注我失败的问题。
真正的目标是委托接口
实现到子对象。这个
问题包含我的详细尝试
解决问题
TAggregatedObject
。你甚至没有
看看我的其他两种解决方案模式
其中一个遭受循环
推荐计数,并打破
IUnknown
等价规则。
罗伯肯尼迪可能还记得;并问道
我提出一个要求问题的问题
解决问题的方法,而不是问题
解决我的一个问题
解决方案。
编辑: grammerified
编辑2: 没有机器人控制器这样的东西。好吧,有 - 我一直与Funuc RJ2控制器合作。但不是在这个例子中!
编辑3 *
TRobotStream = class(TAggregatedObject, IStream)
public
{ IStream }
function Seek(dlibMove: Largeint; dwOrigin: Longint;
out libNewPosition: Largeint): HResult; stdcall;
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;
function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
end;
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
destructor Destroy; override;
property Stream: IStream read GetStream implements IStream;
end;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
rs: IStream;
begin
rs := TRobot.Create;
LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream
rs := nil;
end;
procedure TForm1.LoadRobotFromDatabase(rs: IStream);
begin
rs.Revert; //dummy method call, just to prove we can call it
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
这里的问题是“父母” TRobot
对象在调用期间被销毁:
FStream := TRobotStream.Create(Self);
您必须为创建的子对象添加字段实例:
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
property Stream: IStream read GetStream implements IStream;
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
更新
正如您已经猜到的那样,TRobotStream应该来自TAggregatedObject。声明应该是:
type
TRobotStream = class(TAggregatedObject, IStream)
...
end;
没有必要提到IUnknown。
在TRobot.GetStream中 result := FStream
是一个含蓄的 FStream as IStream
所以写出来也没有必要。
FStream必须声明为TRobotStream而不是IStream,因此当TRobot实例被销毁时它可以被销毁。注意:TAggregatedObject没有引用计数,因此容器必须处理它的生命周期。
更新(Delphi 5代码):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, activex, comobj;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
procedure LoadRobotFromDatabase(rs: IStream);
public
end;
type
TRobotStream = class(TAggregatedObject, IStream)
public
{ IStream }
function Seek(dlibMove: Largeint; dwOrigin: Longint;
out libNewPosition: Largeint): HResult; stdcall;
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;
function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
end;
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
destructor Destroy; override;
property Stream: IStream read GetStream implements IStream;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rs: IStream;
begin
rs := TRobot.Create;
LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream
rs := nil;
end;
procedure TForm1.LoadRobotFromDatabase(rs: IStream);
begin
rs.Revert; //dummy method call, just to prove we can call it
end;
function TRobotStream.Clone(out stm: IStream): HResult;
begin
end;
function TRobotStream.Commit(grfCommitFlags: Integer): HResult;
begin
end;
function TRobotStream.CopyTo(stm: IStream; cb: Largeint; out cbRead, cbWritten: Largeint): HResult;
begin
end;
function TRobotStream.LockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Read(pv: Pointer; cb: Integer; pcbRead: PLongint): HResult;
begin
end;
function TRobotStream.Revert: HResult;
begin
end;
function TRobotStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
out libNewPosition: Largeint): HResult;
begin
end;
function TRobotStream.SetSize(libNewSize: Largeint): HResult;
begin
end;
function TRobotStream.Stat(out statstg: TStatStg; grfStatFlag: Integer): HResult;
begin
end;
function TRobotStream.UnlockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Write(pv: Pointer; cb: Integer; pcbWritten: PLongint): HResult;
begin
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
end.
enter code here
您必须为创建的子对象添加字段实例:
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
property Stream: IStream read GetStream implements IStream;
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
更新
正如您已经猜到的那样,TRobotStream应该来自TAggregatedObject。声明应该是:
type
TRobotStream = class(TAggregatedObject, IStream)
...
end;
没有必要提到IUnknown。
在TRobot.GetStream中 result := FStream
是一个含蓄的 FStream as IStream
所以写出来也没有必要。
FStream必须声明为TRobotStream而不是IStream,因此当TRobot实例被销毁时它可以被销毁。注意:TAggregatedObject没有引用计数,因此容器必须处理它的生命周期。
更新(Delphi 5代码):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, activex, comobj;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
procedure LoadRobotFromDatabase(rs: IStream);
public
end;
type
TRobotStream = class(TAggregatedObject, IStream)
public
{ IStream }
function Seek(dlibMove: Largeint; dwOrigin: Longint;
out libNewPosition: Largeint): HResult; stdcall;
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;
function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
end;
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
destructor Destroy; override;
property Stream: IStream read GetStream implements IStream;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rs: IStream;
begin
rs := TRobot.Create;
LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream
rs := nil;
end;
procedure TForm1.LoadRobotFromDatabase(rs: IStream);
begin
rs.Revert; //dummy method call, just to prove we can call it
end;
function TRobotStream.Clone(out stm: IStream): HResult;
begin
end;
function TRobotStream.Commit(grfCommitFlags: Integer): HResult;
begin
end;
function TRobotStream.CopyTo(stm: IStream; cb: Largeint; out cbRead, cbWritten: Largeint): HResult;
begin
end;
function TRobotStream.LockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Read(pv: Pointer; cb: Integer; pcbRead: PLongint): HResult;
begin
end;
function TRobotStream.Revert: HResult;
begin
end;
function TRobotStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
out libNewPosition: Largeint): HResult;
begin
end;
function TRobotStream.SetSize(libNewSize: Largeint): HResult;
begin
end;
function TRobotStream.Stat(out statstg: TStatStg; grfStatFlag: Integer): HResult;
begin
end;
function TRobotStream.UnlockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Write(pv: Pointer; cb: Integer; pcbWritten: PLongint): HResult;
begin
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
end.
enter code here
您的类不需要委派从任何特定类继承。只要已经实现了适当的方法,您就可以从TObject继承。我将保持简单并使用TInterfacedObject进行说明,它提供了您已经识别的3种核心方法。
此外,你不应该需要 TRobotStream = class(TAggregatedObject, IUnknown, IStream)
。您可以简单地声明IStream继承自IUnknown。顺便说一句,我总是给我的接口一个GUID(按Ctrl + Shit + G)。
根据您的特定需求,可以应用许多不同的方法和技术。
最简单的委托是通过接口。
TRobotStream = class(TinterfacedObject, IStream)
TRobot = class(TInterfacedObject, IStream)
private
//The delegator delegates the implementations of IStream to the child object.
//Ensure the child object is created at an appropriate time before it is used.
FRobotStream: IStream;
property RobotStream: IStream read FRobotStream implements IStream;
end;
也许有一些事情需要注意:
- 确保您委派的对象具有适当的生命周期。
- 务必保留对被请求者的引用。请记住,接口是引用计数,并且一旦计数降至零就会被销毁。 这实际上可能是您头痛的原因。