我如何从 TFileStream 和 TMemoryStream 继承?
How can I descend from both TFileStream and TMemoryStream?
我有一个继承自 TFileStream 的 class 和一个继承自 TMemoryStream 的 class。两者都实现了与读取数据完全相同的功能,例如:
TCustomFileStream = class (TFileStream)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
etc
当我想编写一个可以将任一类型的流作为参数的函数时,我必须使用 TStream:
function DoStuff(SourceStream: TStream);
这当然意味着我不能使用我的自定义函数。
处理这个问题的最佳方法是什么?理想情况下,我希望能够有一个兼容 Tstream 的 class,它可以在 FileStream 或 MemoryStream 上工作,这样我就可以做这样的事情,流是 FileStream 还是 MemoryStream 都没有关系:
function DoStuff(SourceStream: TMyCustomStream);
begin
data := SourceStream.ReadDWord;
otherData := SourceStream.Read(Buffer, 20);
end;
您可以有一个单独的 reader class 在(抽象)流上运行。看看例如在 Classes
中的 TBinaryReader
寻找灵感。
Delphi 不支持多重继承,在这种情况下没有意义。
可能的解决方案
你可以做的是编写一个 class 实现 TStream
并接受可能是 TFileStream
或 TMemoryStream
的内部流。像这样:
class TMyStream = class(TStream)
private
InternalStream: TStream;
public
constructor Create(InternalStream:TStream);
/// implement all TStream abstract read, write, and seek methods and call InternalStream methods inside them.
/// implement your custom methods here
end;
constructor TMyStream.Create(InternalStream:TStream)
begin
Self.InternalStream=InternalStream;
end;
这样您就可以得到您所需要的 class;支持流默认方法和您的自定义方法。
可选
如果您的自定义 TFileStream
和 TMemoryStream
必须有两个不同的 classes,那么您可以这样做:
class TMyFileStream : TMyStream
public
constructor Create(const Path:String); reintroduce;
end
constructor TMyFileStream.Create(const Path:String);
begin
inherited Create(TFileStream.Create(Path));
end;
这些解决方法只是一些想法,可帮助您接近您想要的结果。修改它们以满足您的需求。
首先:多重继承在Delphi中是不可能的。
你说你的自定义流 classes 的方法对它们都是一样的实现?您可以使用流形式的装饰器模式 reader class.
另一方面,您可以通过为它编写 class 帮助程序来扩展 TStream
:
TCustomStreamHelper = class helper for TStream
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
// etc.
end;
因此,在编译器已知 TCustomStreamHelper
的每个单元中(因为您将其单元添加到 uses
子句),您可以使用 TStream
就像它有那些额外的几个世纪以来的方法。
您可以将常用方法放在 interface
中,并在每个后代 类 中实现 QueryInterface
、_AddRef
和 _Release
方法。
参见 Delphi interfaces without reference counting。
type
IStreamInterface = interface
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
end;
TCustomFileStream = class (TFileStream, IStreamInterface)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
TCustomMemoryStream = class (TMemoryStream, IStreamInterface)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
... 并为过程使用 IStreamInterface
类型的参数:
procedure DoStuff(SourceStream: IStreamInterface);
var
data: Word;
begin
data := SourceStream.ReadDWord;
end;
要回答实际问题标题中的问题:你不能。 :)
但如果我们退后一步,看看您试图解决的问题:
I have a class that inherits from TFileStream and a class that
inherits from TMemoryStream. Both implement exactly the same functions
to do with reading data
我认为您 mis-stated 遇到了您的问题并且 re-stating 它正确地指向了您需要的答案。 :)
I have some structured data that I need to read from different sources (different stream classes).
流就是一堆字节。这些字节中的任何结构都由 如何 您 read/write 流决定。即在这种情况下,"how" 体现在您的函数中。事实上,涉及的具体流 classes 是 TFileStream 和 TMemoryStream 从根本上不是问题的一部分。解决 TStream 的问题,你就解决了所有 TStream 派生的 classes,包括你现在正在处理的问题。
Stream classes 应该根据它们需要 read/write bytes 进出特定位置(内存、文件、字符串等)的方式进行专门化这些字节中没有任何特定的结构。
与其创建必须复制结构知识的专用流 classes,您真正需要的是一个 class,它封装了所涉及数据的结构知识,并且是能够将其应用于任何流。
一个ReaderClass
一种方法(最好的?)是实现一个 class 来封装您需要的行为。例如在 "reader" class.
TStuffReader = class
private
fStream: TStream;
public
constructor Create(aStream: TStream);
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
end;
这个 class 也可能提供将 写入 流的功能(在这种情况下,您可以将其称为 TStuffFiler,例如,因为它不会只是一个 reader),或者您可能有一个单独的 class 用于写入,称为 TStuffWriter(例如)。
无论您选择如何实现它,此 reader class 将能够读取(and/or 写入)结构化数据 from/to any TStream导出class。对于这些函数来说,涉及什么特定的 class 流应该无关紧要。
如果您的问题包括需要将对流的引用传递给各种函数等,那么您可以传递对 reader [=119= 的引用]. reader 必然带有对所涉及流的引用,但之前您认为需要调用专用流 classes 上的函数的代码反而简单地使用了 reader 函数。如果该代码还需要访问流本身,那么 reader 可以在必要时公开它。
然后将来如果您发现自己需要从其他流 classes 中读取此类数据(例如 TBLOBStream 如果您发现自己从中检索数据一个数据库)你的TStuffReader(或者你选择的任何名称)可以直接介入并为你完成工作,而无需你做任何进一步的工作。
Class帮手Non-Alternative
Class 助手似乎提供了一种近似 "multiple inheritance" 的机制,但应避免使用。它们从未打算用于应用程序代码。
VCL 中 class 助手的存在完全符合您的预期,因为它们旨在用于框架和库中,而 VCL 是一个框架库,因此它的使用与该用法完全一致.
但这不是认可它们适合在应用程序代码中使用并且文档继续强调这一点。
Class and record helpers provide a way to extend a type, but they
should not be viewed as a design tool to be used when developing new
code. For new code you should always rely on normal class inheritance
and interface implementations.
文档也非常清楚地说明了适用于 class 助手的限制,但没有清楚地解释 为什么 这些会导致问题,这也许就是为什么一些人们仍然坚持认为它们适合使用。
我covered these problems in a blog post not long after they were introduced (and the same problems still apply today). In fact, it is such an issue that I wrote a number of posts covering the topic.
似乎不愿意放弃这样一种观念,即只要你小心对待你的助手,你就不会运行出问题,这是忽略了一个事实,无论怎样小心 你 是,如果你最终与他们共享代码,其他人同样小心使用,你对助手的使用可能会被破坏。
Delphi 中的共享代码不比 VCL 本身多。
在 VCL 中使用(额外的)助手会使 运行宁与您自己的助手陷入麻烦的可能性 更多,而不是 少。即使您的代码在一个版本的 VCL 中与您自己的助手一起工作得很好,下一个版本也可能会出现问题。
远非建议 更多 使用助手,它们在 VCL 中的扩散只是您应该避免[=84= 的一个很好的理由] 他们
我有一个继承自 TFileStream 的 class 和一个继承自 TMemoryStream 的 class。两者都实现了与读取数据完全相同的功能,例如:
TCustomFileStream = class (TFileStream)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
etc
当我想编写一个可以将任一类型的流作为参数的函数时,我必须使用 TStream:
function DoStuff(SourceStream: TStream);
这当然意味着我不能使用我的自定义函数。 处理这个问题的最佳方法是什么?理想情况下,我希望能够有一个兼容 Tstream 的 class,它可以在 FileStream 或 MemoryStream 上工作,这样我就可以做这样的事情,流是 FileStream 还是 MemoryStream 都没有关系:
function DoStuff(SourceStream: TMyCustomStream);
begin
data := SourceStream.ReadDWord;
otherData := SourceStream.Read(Buffer, 20);
end;
您可以有一个单独的 reader class 在(抽象)流上运行。看看例如在 Classes
中的 TBinaryReader
寻找灵感。
Delphi 不支持多重继承,在这种情况下没有意义。
可能的解决方案
你可以做的是编写一个 class 实现 TStream
并接受可能是 TFileStream
或 TMemoryStream
的内部流。像这样:
class TMyStream = class(TStream)
private
InternalStream: TStream;
public
constructor Create(InternalStream:TStream);
/// implement all TStream abstract read, write, and seek methods and call InternalStream methods inside them.
/// implement your custom methods here
end;
constructor TMyStream.Create(InternalStream:TStream)
begin
Self.InternalStream=InternalStream;
end;
这样您就可以得到您所需要的 class;支持流默认方法和您的自定义方法。
可选
如果您的自定义 TFileStream
和 TMemoryStream
必须有两个不同的 classes,那么您可以这样做:
class TMyFileStream : TMyStream
public
constructor Create(const Path:String); reintroduce;
end
constructor TMyFileStream.Create(const Path:String);
begin
inherited Create(TFileStream.Create(Path));
end;
这些解决方法只是一些想法,可帮助您接近您想要的结果。修改它们以满足您的需求。
首先:多重继承在Delphi中是不可能的。
你说你的自定义流 classes 的方法对它们都是一样的实现?您可以使用流形式的装饰器模式 reader class.
另一方面,您可以通过为它编写 class 帮助程序来扩展 TStream
:
TCustomStreamHelper = class helper for TStream
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
// etc.
end;
因此,在编译器已知 TCustomStreamHelper
的每个单元中(因为您将其单元添加到 uses
子句),您可以使用 TStream
就像它有那些额外的几个世纪以来的方法。
您可以将常用方法放在 interface
中,并在每个后代 类 中实现 QueryInterface
、_AddRef
和 _Release
方法。
参见 Delphi interfaces without reference counting。
type
IStreamInterface = interface
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
end;
TCustomFileStream = class (TFileStream, IStreamInterface)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
TCustomMemoryStream = class (TMemoryStream, IStreamInterface)
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
... 并为过程使用 IStreamInterface
类型的参数:
procedure DoStuff(SourceStream: IStreamInterface);
var
data: Word;
begin
data := SourceStream.ReadDWord;
end;
要回答实际问题标题中的问题:你不能。 :)
但如果我们退后一步,看看您试图解决的问题:
I have a class that inherits from TFileStream and a class that inherits from TMemoryStream. Both implement exactly the same functions to do with reading data
我认为您 mis-stated 遇到了您的问题并且 re-stating 它正确地指向了您需要的答案。 :)
I have some structured data that I need to read from different sources (different stream classes).
流就是一堆字节。这些字节中的任何结构都由 如何 您 read/write 流决定。即在这种情况下,"how" 体现在您的函数中。事实上,涉及的具体流 classes 是 TFileStream 和 TMemoryStream 从根本上不是问题的一部分。解决 TStream 的问题,你就解决了所有 TStream 派生的 classes,包括你现在正在处理的问题。
Stream classes 应该根据它们需要 read/write bytes 进出特定位置(内存、文件、字符串等)的方式进行专门化这些字节中没有任何特定的结构。
与其创建必须复制结构知识的专用流 classes,您真正需要的是一个 class,它封装了所涉及数据的结构知识,并且是能够将其应用于任何流。
一个ReaderClass
一种方法(最好的?)是实现一个 class 来封装您需要的行为。例如在 "reader" class.
TStuffReader = class
private
fStream: TStream;
public
constructor Create(aStream: TStream);
function ReadByte: byte;
function ReadWord: word;
function ReadWordBE: word;
function ReadDWord: longword;
function ReadDWordBE: longword;
function ReadString(Length: integer): string;
function ReadBlockName: string;
end;
这个 class 也可能提供将 写入 流的功能(在这种情况下,您可以将其称为 TStuffFiler,例如,因为它不会只是一个 reader),或者您可能有一个单独的 class 用于写入,称为 TStuffWriter(例如)。
无论您选择如何实现它,此 reader class 将能够读取(and/or 写入)结构化数据 from/to any TStream导出class。对于这些函数来说,涉及什么特定的 class 流应该无关紧要。
如果您的问题包括需要将对流的引用传递给各种函数等,那么您可以传递对 reader [=119= 的引用]. reader 必然带有对所涉及流的引用,但之前您认为需要调用专用流 classes 上的函数的代码反而简单地使用了 reader 函数。如果该代码还需要访问流本身,那么 reader 可以在必要时公开它。
然后将来如果您发现自己需要从其他流 classes 中读取此类数据(例如 TBLOBStream 如果您发现自己从中检索数据一个数据库)你的TStuffReader(或者你选择的任何名称)可以直接介入并为你完成工作,而无需你做任何进一步的工作。
Class帮手Non-Alternative
Class 助手似乎提供了一种近似 "multiple inheritance" 的机制,但应避免使用。它们从未打算用于应用程序代码。
VCL 中 class 助手的存在完全符合您的预期,因为它们旨在用于框架和库中,而 VCL 是一个框架库,因此它的使用与该用法完全一致.
但这不是认可它们适合在应用程序代码中使用并且文档继续强调这一点。
Class and record helpers provide a way to extend a type, but they should not be viewed as a design tool to be used when developing new code. For new code you should always rely on normal class inheritance and interface implementations.
文档也非常清楚地说明了适用于 class 助手的限制,但没有清楚地解释 为什么 这些会导致问题,这也许就是为什么一些人们仍然坚持认为它们适合使用。
我covered these problems in a blog post not long after they were introduced (and the same problems still apply today). In fact, it is such an issue that I wrote a number of posts covering the topic.
似乎不愿意放弃这样一种观念,即只要你小心对待你的助手,你就不会运行出问题,这是忽略了一个事实,无论怎样小心 你 是,如果你最终与他们共享代码,其他人同样小心使用,你对助手的使用可能会被破坏。
Delphi 中的共享代码不比 VCL 本身多。
在 VCL 中使用(额外的)助手会使 运行宁与您自己的助手陷入麻烦的可能性 更多,而不是 少。即使您的代码在一个版本的 VCL 中与您自己的助手一起工作得很好,下一个版本也可能会出现问题。
远非建议 更多 使用助手,它们在 VCL 中的扩散只是您应该避免[=84= 的一个很好的理由] 他们