为什么Delphi中的TStreamAdapter类在众多组件中显得如此低调且不常被提及?

2026-04-10 02:321阅读0评论SEO资源
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计765个文字,预计阅读时间需要4分钟。

为什么Delphi中的TStreamAdapter类在众多组件中显得如此低调且不常被提及?

比较这两个片段,可以看出:

1. 第一个片段包含了对对象 `d` 进行序列化和反序列化的操作。它使用 `Save` 方法将对象保存到名为 `test.bin` 的文件中,并使用 `Load` 方法从该文件中恢复对象。

2.两个操作都使用 `TStreamAdapter.Create` 方法来创建流适配器,并指定了 `soOwned` 参数,这意味着创建的流将在使用完毕后被释放。

3.`Save` 方法中传递了 `true` 参数,表示调用 `Load` 方法时将保留流的状态。

具体代码如下:

csharp

(d as IPersistStream).Save(TStreamAdapter.Create(TFileStream.Create('test.bin', fmCreate), soOwned), true);(d as IPersistStream).Load(TStreamAdapter.Create(TFileStream.Create('test.bin', fmOpenRead), soOwned));

比较这两个片段:

(d as IPersistStream).Save( TStreamAdapter.Create( TFileStream.Create('test.bin',fmCreate),soOwned),true); (d as IPersistStream).Load( TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned));

这在第二个TFileStream.Create上失败,因为第一个没有被销毁.这很奇怪,因为参数有唯一的引用,我认为它会在关闭Save调用时被破坏.所以我尝试了这个:

var x:IStream; begin x:=TStreamAdapter.Create( TFileStream.Create('test.bin',fmCreate),soOwned); (d as IPersistStream).Save(x,true); x:=nil; x:=TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned); (d as IPersistStream).Load(x); x:=nil;

哪个工作正常. (但是如果没有x:= nil则再次失败;)所以不要担心d,它是一个IPersistStream并且行为正常.为什么强制_Release调用需要一个明确的nil赋值?这是Delphi 7的已知问题吗?是因为链接器/编译器切换?

这是IPersistStream.Save的声明:

function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;

关键点是stream参数作为const传递.这意味着Save函数不会引用IStream接口.它的引用计数既不递增也不递减.既然都没有发生,它永远不会被摧毁.

解决它的方法是确保某些东西包含对接口的引用.这是您在第二个示例中演示的内容.

为什么Delphi中的TStreamAdapter类在众多组件中显得如此低调且不常被提及?

您需要赋值为nil的原因是这个代码的执行顺序:

x := TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned );

它按此顺序发生:

> TFileStream.Create.
> TStreamAdapter.Create.
> x._释放清除旧参考.
>参考新的IStream.

这显然是错误的顺序.在调用TFileStream.Create之前,需要清除x.

根据前Embarcadero编译工程师Barry Kelly的说法,the issue regarding the interface passed to a const parameter is a bug.它从来没有得到修复,我一个人已经放弃了对这种情况发生的希望.

我的SSCCE来证明这个问题在这里:

program SO22846335; {$APPTYPE CONSOLE} type TMyInterfaceObject = class(TObject, IInterface) FRefCount: Integer; FName: string; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; constructor Create(const Name: string); destructor Destroy; override; end; constructor TMyInterfaceObject.Create(const Name: string); begin inherited Create; FName := Name; Writeln(FName + ' created'); end; destructor TMyInterfaceObject.Destroy; begin Writeln(FName + ' destroyed'); inherited; end; function TMyInterfaceObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin Result := E_NOINTERFACE; end; function TMyInterfaceObject._AddRef: Integer; begin Writeln(FName + ' _AddRef'); Result := AtomicIncrement(FRefCount); end; function TMyInterfaceObject._Release: Integer; begin Writeln(FName + ' _Release'); Result := AtomicDecrement(FRefCount); if Result = 0 then Destroy; end; procedure Foo(const Intf: IInterface); begin Writeln('Foo'); end; procedure Bar(Intf: IInterface); begin Writeln('Bar'); end; begin Foo(TMyInterfaceObject.Create('Instance1')); Bar(TMyInterfaceObject.Create('Instance2')); Readln; end.

产量

Instance1 created Foo Instance2 created Instance2 _AddRef Bar Instance2 _Release Instance2 destroyed

本文共计765个文字,预计阅读时间需要4分钟。

为什么Delphi中的TStreamAdapter类在众多组件中显得如此低调且不常被提及?

比较这两个片段,可以看出:

1. 第一个片段包含了对对象 `d` 进行序列化和反序列化的操作。它使用 `Save` 方法将对象保存到名为 `test.bin` 的文件中,并使用 `Load` 方法从该文件中恢复对象。

2.两个操作都使用 `TStreamAdapter.Create` 方法来创建流适配器,并指定了 `soOwned` 参数,这意味着创建的流将在使用完毕后被释放。

3.`Save` 方法中传递了 `true` 参数,表示调用 `Load` 方法时将保留流的状态。

具体代码如下:

csharp

(d as IPersistStream).Save(TStreamAdapter.Create(TFileStream.Create('test.bin', fmCreate), soOwned), true);(d as IPersistStream).Load(TStreamAdapter.Create(TFileStream.Create('test.bin', fmOpenRead), soOwned));

比较这两个片段:

(d as IPersistStream).Save( TStreamAdapter.Create( TFileStream.Create('test.bin',fmCreate),soOwned),true); (d as IPersistStream).Load( TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned));

这在第二个TFileStream.Create上失败,因为第一个没有被销毁.这很奇怪,因为参数有唯一的引用,我认为它会在关闭Save调用时被破坏.所以我尝试了这个:

var x:IStream; begin x:=TStreamAdapter.Create( TFileStream.Create('test.bin',fmCreate),soOwned); (d as IPersistStream).Save(x,true); x:=nil; x:=TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned); (d as IPersistStream).Load(x); x:=nil;

哪个工作正常. (但是如果没有x:= nil则再次失败;)所以不要担心d,它是一个IPersistStream并且行为正常.为什么强制_Release调用需要一个明确的nil赋值?这是Delphi 7的已知问题吗?是因为链接器/编译器切换?

这是IPersistStream.Save的声明:

function Save(const stm: IStream; fClearDirty: BOOL): HResult; stdcall;

关键点是stream参数作为const传递.这意味着Save函数不会引用IStream接口.它的引用计数既不递增也不递减.既然都没有发生,它永远不会被摧毁.

解决它的方法是确保某些东西包含对接口的引用.这是您在第二个示例中演示的内容.

为什么Delphi中的TStreamAdapter类在众多组件中显得如此低调且不常被提及?

您需要赋值为nil的原因是这个代码的执行顺序:

x := TStreamAdapter.Create( TFileStream.Create('test.bin',fmOpenRead),soOwned );

它按此顺序发生:

> TFileStream.Create.
> TStreamAdapter.Create.
> x._释放清除旧参考.
>参考新的IStream.

这显然是错误的顺序.在调用TFileStream.Create之前,需要清除x.

根据前Embarcadero编译工程师Barry Kelly的说法,the issue regarding the interface passed to a const parameter is a bug.它从来没有得到修复,我一个人已经放弃了对这种情况发生的希望.

我的SSCCE来证明这个问题在这里:

program SO22846335; {$APPTYPE CONSOLE} type TMyInterfaceObject = class(TObject, IInterface) FRefCount: Integer; FName: string; function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall; function _AddRef: Integer; stdcall; function _Release: Integer; stdcall; constructor Create(const Name: string); destructor Destroy; override; end; constructor TMyInterfaceObject.Create(const Name: string); begin inherited Create; FName := Name; Writeln(FName + ' created'); end; destructor TMyInterfaceObject.Destroy; begin Writeln(FName + ' destroyed'); inherited; end; function TMyInterfaceObject.QueryInterface(const IID: TGUID; out Obj): HResult; begin Result := E_NOINTERFACE; end; function TMyInterfaceObject._AddRef: Integer; begin Writeln(FName + ' _AddRef'); Result := AtomicIncrement(FRefCount); end; function TMyInterfaceObject._Release: Integer; begin Writeln(FName + ' _Release'); Result := AtomicDecrement(FRefCount); if Result = 0 then Destroy; end; procedure Foo(const Intf: IInterface); begin Writeln('Foo'); end; procedure Bar(Intf: IInterface); begin Writeln('Bar'); end; begin Foo(TMyInterfaceObject.Create('Instance1')); Bar(TMyInterfaceObject.Create('Instance2')); Readln; end.

产量

Instance1 created Foo Instance2 created Instance2 _AddRef Bar Instance2 _Release Instance2 destroyed