我有一个继承自TFileStream的类和一个继承自TMemoryStream的类。两者都实现与读取数据完全相同的功能,例如:
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);
这当然意味着我无法使用自定义功能。 什么是解决这个问题的最好方法?理想情况下,我希望能够有一个兼容FileStream或MemoryStream的Tstream兼容类,所以我可以做这样的事情,如果流是FileStream或MemoryStream则无关紧要:
function DoStuff(SourceStream: TMyCustomStream);
begin
data := SourceStream.ReadDWord;
otherData := SourceStream.Read(Buffer, 20);
end;
答案 0 :(得分:6)
首先:Delphi中无法实现多重继承。
您说自定义流类的方法是否同样适用于它们?您可以以流阅读器类的形式使用装饰器模式。
另一方面,你可以通过为它编写一个类助手来扩展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
,就像它有其他方法一样几个世纪。
答案 1 :(得分:6)
要回答实际问题标题中的问题:您无法。 :)
但是,如果我们退一步看看你正在尝试解决的问题:
我有一个继承自TFileStream的类和一个类 继承自TMemoryStream。两者都实现完全相同的功能 与阅读数据有关
我认为您错误地陈述了您的问题并正确地重新说明了您需要的答案。 :)
I have some structured data that I need to read from different sources (different stream classes).
流只是一堆字节。这些字节中的任何结构都由如何读取/写入流来确定。即在这种情况下,"如何"体现在你的功能中。所涉及的具体流类是 TFileStream 和 TMemoryStream 这一事实并不是问题的根本原因。解决 TStream 的问题,然后解决所有 TStream 派生类,包括你现在正在处理的类。
流类应根据他们如何在特定位置(内存,文件,字符串等)中读取/写入 bytes 而不是任何特定的结构来专门化那些字节。
不是创建必须复制结构知识的专用流类,而是真正需要的是一个类,它封装了所涉及数据结构的知识,并能够将其应用于任何 stream。
这种方法(最好的?)是实现一个封装所需行为的类。例如,在"读者"类。
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;
此类也可能提供将写入流的功能(例如,在这种情况下,您可以将其称为 TStuffFiler ,因为它不会只是一个读者)或者你可能有一个单独的写作类叫做 TStuffWriter (例如)。
但是,您选择实现它,此读者类将能够从任何 TStream 派生类读取(和/或写入)结构化数据。对于这些函数而言,涉及特定类的流是无关紧要的。
如果您的问题包括需要将对流的引用传递给各种函数等,那么您将传递对 reader 类的引用。读者必须带有对所涉及的流的引用,但是之前您认为需要在专用流类上调用函数的代码只是使用读取器函数。如果该代码还需要访问流本身,则读者可以在必要时公开它。
接下来如果您发现自己需要从其他流类中读取此类数据(例如,如果您发现自己从数据库中检索数据,则为 TBLOBStream ) TStuffReader (或者您选择称之为的任何东西)可以直接为您完成工作而无需您做任何进一步的工作。
类帮助程序可能似乎提供了一种机制来近似"多重继承"但应该避免。它们从未打算用于应用程序代码。
VCL中类助手的存在正如您所期望的那样,因为它们旨在用于框架和库,而VCL是一个框架库,因此它的使用与该用法完全一致。 / p>
但不认可它们适合在应用程序代码中使用,文档继续强制执行这一点。
类和记录助手提供了一种扩展类型的方法,但它们 不应被视为开发新产品时使用的设计工具 码。对于新代码,您应该始终依赖于普通的类继承 和接口实现。
文档也很清楚适用于助手的限制,但没有明确解释为什么这些会导致问题,这也许是为什么有些人仍然坚持认为它们适合使用
我covered these problems in a blog post在他们介绍之后不久(同样的问题今天仍然适用)。事实上,我写了a number of posts covering the topic就是这样一个问题。
似乎不愿意放弃这样一种观念,即只要你对你的助手小心,那么你就不会遇到问题,这就是忽略了这样一个事实:无论多么小心 ,如果您最终与他们共享代码,其他人同样谨慎使用可以打破您对帮助者的使用。
Delphi中没有比VCL本身更多的共享代码。
在VCL中使用(附加)帮助程序会使您自己的帮助程序更多可能会遇到麻烦,而不是 less 。即使你的代码在一个版本的VCL中与你自己的助手完美配合,下一个版本也可能会破坏。
它不仅仅是建议更多使用帮助程序,它们在VCL中的扩散只是你应该避免它们的一个很好的理由。
答案 2 :(得分:4)
您可以在(抽象)流上运行单独的阅读器类。看看,例如在Classes
if(genderImage==null){
genderImage= //An Image to use when user select no gender before
//pressing okay button
}
initComponents();
playerBattle.setIcon(genderImage);
获取灵感。
答案 3 :(得分:0)
Delphi不支持多重继承,在这种情况下它没有意义。
您可以做的是编写一个实现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;
或TFileStream
的内部流。像这样:
TMemoryStream
这样你就拥有了所需的确切类别;支持流默认方法和自定义方法。
如果您的自定义class TMyFileStream : TMyStream
public
constructor Create(const Path:String); reintroduce;
end
constructor TMyFileStream.Create(const Path:String);
begin
inherited Create(TFileStream.Create(Path));
end;
和{{1}}必须有两个不同的类,那么您可以执行以下操作:
{{1}}
这些解决方法只是帮助您接近所需内容的一些想法。修改它们以使它们满足您的需求。
答案 4 :(得分:0)
您可以将常用方法放在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;