Ian*_*oyd 11 delphi interface delegation delphi-5
我有一个对象,它将一个特别复杂的接口的实现委托给子对象.这正是我认为的工作TAggregatedObject." child "对象维护对其" 控制器 " 的弱引用,并且所有QueryInterface请求都传递回父级.这样可以保持IUnknown
始终为同一对象的规则.
所以,我的父(即"Controller")对象声明它实现了IStream接口:
type
TRobot = class(TInterfacedObject, IStream)
private
function GetStream: IStream;
public
property Stream: IStream read GetStrem implements IStream;
end;
Run Code Online (Sandbox Code Playgroud)
注意:这是一个假设的例子.我选择这个词
Robot是因为它听起来很复杂,并且单词只有5个字母 - 它很短.我也选择IStream因为它的短.我打算使用IPersistFile或IPersistFileInit,但它们更长,并使示例代码更难实现.换句话说:这是一个假设的例子.
现在我有我的子对象将实现IStream:
type
TRobotStream = class(TAggregatedObject, IStream)
public
...
end;
Run Code Online (Sandbox Code Playgroud)
所有剩下的,这就是我的问题开始的地方:创建RobotStream它被要求的时间:
function TRobot.GetStream: IStream;
begin
Result := TRobotStream.Create(Self) as IStream;
end;
Run Code Online (Sandbox Code Playgroud)
此代码无法编译,错误Operator not applicable to this operand type..
这是因为delphi正在尝试对as IStream未实现的对象执行IUnknown:
TAggregatedObject = class
...
{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
...
Run Code Online (Sandbox Code Playgroud)
IUnknown 方法可能存在,但该对象不会通告它支持IUnknown.没有IUnknown接口,Delphi无法调用QueryInterface执行转换.
所以我改变我的TRobotStream类来宣传它实现了缺少的接口(它做了;它从它的祖先继承它):
type
TRobotStream = class(TAggregatedObject, IUnknown, IStream)
...
Run Code Online (Sandbox Code Playgroud)
现在它编译,但在运行时崩溃:
Result := TRobotStream.Create(Self) as IStream;
Run Code Online (Sandbox Code Playgroud)
现在我可以看到什么情况发生,但我无法解释为什么.Delphi IntfClear在我的父Robot对象上调用了从子对象的构造函数出来的路径.
我不知道防止这种情况的正确方法.我可以尝试强迫演员:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Run Code Online (Sandbox Code Playgroud)
并希望保留参考.事实证明它确实保留了引用 - 在构造函数的出路上没有崩溃.
注意:这让我很困惑.因为我传递了一个预期接口的对象.我会假设编译器隐式预先形成类型转换,即:
Result := TRobotStream.Create(Self就像IUnknown);为了满足这个要求.语法检查器没有抱怨的事实让我认为一切都是正确的.
但崩溃还没有结束.我把线改为:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Run Code Online (Sandbox Code Playgroud)
并且代码确实从构造函数返回TRobotStream而没有破坏我的父对象,但现在我得到了堆栈溢出.
原因是TAggregatedObject将所有QueryInterface(即类型转换)推迟回父对象.在我的情况下,我正在铸造TRobotStream一个IStream.
当我在最后询问TRobotStream它IStream时:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Run Code Online (Sandbox Code Playgroud)
它转过来并询问其控制器的IStream接口,这会触发对以下内容的调用:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Run Code Online (Sandbox Code Playgroud)
转过来并打电话:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Result := TRobotStream.Create(Self as IUnknown) as IStream;
Run Code Online (Sandbox Code Playgroud)
繁荣! 堆栈溢出.
盲目地,我尝试删除最终的强制转换IStream,让Delphi尝试隐含地将对象强制转换为接口(我刚刚看到上面的代码无效):
Result := TRobotStream.Create(Self as IUnknown);
Run Code Online (Sandbox Code Playgroud)
现在没有崩溃; 我对此不太了解.我构建了一个对象,一个支持多个接口的对象.现在,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等价规则.罗伯肯尼迪可能还记得; 并要求我提出一个问题,要求解决问题,而不是解决我的一个解决方案中的问题.
编辑:语法化
编辑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;
Run Code Online (Sandbox Code Playgroud)
这里的问题是"父" TRobot对象在调用期间被销毁:
FStream := TRobotStream.Create(Self);
Run Code Online (Sandbox Code Playgroud)
您必须为创建的子对象添加字段实例:
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;
Run Code Online (Sandbox Code Playgroud)
正如您已经猜到的那样,更新 TRobotStream应该从TAggregatedObject派生.声明应该是:
type
TRobotStream = class(TAggregatedObject, IStream)
...
end;
Run Code Online (Sandbox Code Playgroud)
没有必要提到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.
Run Code Online (Sandbox Code Playgroud)