Delphi &引用;降序;德尔福唱片公司?
我知道你不能从记录中得出任何东西,但我不知道如何用一句话来总结我的问题。如果需要,请编辑标题 我想在这里做的是创建一个泛型类型的数组,它可以是X个类型中的一个,数组将填充这些自定义类型(它们有不同的字段,这很重要)。简单的方法是只创建一个变量记录数组,每个变量都有自己的类型,但显然不能像这样重新声明标识符:Delphi &引用;降序;德尔福唱片公司?,delphi,records,Delphi,Records,我知道你不能从记录中得出任何东西,但我不知道如何用一句话来总结我的问题。如果需要,请编辑标题 我想在这里做的是创建一个泛型类型的数组,它可以是X个类型中的一个,数组将填充这些自定义类型(它们有不同的字段,这很重要)。简单的方法是只创建一个变量记录数组,每个变量都有自己的类型,但显然不能像这样重新声明标识符: GenericRec = Record case SubTypeName: TSubTypeName of type1name: (SubRec: Type1); typ
GenericRec = Record
case SubTypeName: TSubTypeName of
type1name: (SubRec: Type1);
type2name: (SubRec: Type2);
...
typeNname: (SubRec: TypeN);
end;
正在将SubRec
更改为SubRec1、SubRec2。。。SubRecN
使引用变得痛苦,但并非不可能。
自从我开始寻找解决上述问题的替代方案后,我想到了课堂
我试图实现的一个明显的例子是TObject
,一个数组可以分配给许多不同的事物。这正是我想要的,但对于记录(这是不可能的),因为我希望能够将记录保存到文件中并将其读回(也因为这是我已经熟悉的东西)。创建我自己的简单类不是问题,从中创建一个后代类来表示我的子类型-我可以做到这一点。但是把它写进文件并读回怎么样?这归结为序列化,我不知道怎么做。据我所知,这并不容易,类必须从TComponent
派生而来
TMyClass = Class
如果我像上面那样上课有什么区别吗?它没有什么特别之处,最多有10个字段,包括一些自定义类型
撇开序列化不谈(只是因为我有很多关于这个主题的阅读资料要做),在这里使用类也可能是不可能的
在这一点上,我的选择是什么?我应该放弃记录,在课堂上尝试吗?或者仅仅坚持记录和处理变异的“限制”就不会那么复杂了?我的一切都是为了学习,如果课堂教学法能让我变得更聪明,我会去做的。我也刚刚查看了
TList
(从未使用过),但似乎它与记录的结合不太好,也许可以做到,但这可能是我目前无法做到的。我愿意接受任何建议。我该怎么办?要对记录执行此操作,您需要创建前面有公共字段的不同记录类型,然后将这些相同字段放在通用记录中。然后,您可以在需要时将指向泛型记录的指针转换为指向特定记录的指针。例如:
type
PGenericRec = ^GenericRec;
GenericRec = Record
RecType: Integer;
end;
PType1Rec = ^Type1Rec;
Type1Rec = Record
RecType: Integer;
// Type1Rec specific fields...
end;
PType2Rec = ^Type2Rec;
Type2Rec = Record
RecType: Integer;
// Type2Rec specific fields...
end;
PTypeNRec = ^TypeNRec;
TypeNRec = Record
RecType: Integer;
// TypeNRec specific fields...
end;
var
Recs: array of PGenericRec;
Rec1: PType1Rec;
Rec2: PType2Rec;
RecN: PTypeNRec;
I: Integer;
begin
SetLength(Recs, 3);
New(Rec1);
Rec1^.RecType := RecTypeForType1Rec;
// fill Rec1 fields ...
Recs[0] := PGenericRec(Rec1);
New(Rec2);
Rec2^.RecType := RecTypeForType2Rec;
// fill Rec2 fields ...
Recs[1] := PGenericRec(Rec2);
New(RecN);
Rec3^.RecType := RecTypeForTypeNRec;
// fill RecN fields ...
Recs[2] := PGenericRec(RecN);
for I := 0 to 2 do
begin
case Recs[I]^.RecType of
RecTypeForType1Rec: begin
Rec1 := PType1Rec(Recs[I]);
// use Rec1 as needed...
end;
RecTypeForType1Re2: begin
Rec2 := PType2Rec(Recs[I]);
// use Rec2 as needed...
end;
RecTypeForTypeNRec: begin
RecN := PTypeNRec(Recs[I]);
// use RecN as needed...
end;
end;
end;
for I := 0 to 2 do
begin
case Recs[I]^.RecType of
RecTypeForType1Rec: Dispose(PType1Rec(Recs[I]));
RecTypeForType2Rec: Dispose(PType2Rec(Recs[I]));
RecTypeForTypeNRec: Dispose(PTypeNRec(Recs[I]));
end;
end;
end;
至于序列化,您不需要t组件
。您可以序列化记录,只需手动执行即可。对于写入,请先写出RecType
值,然后写出特定于记录的值。对于读取,请先读取重类型
值,然后为该值创建适当的记录类型,然后将特定于记录的值读入其中:
interface
type
PGenericRec = ^GenericRec;
GenericRec = Record
RecType: Integer;
end;
NewRecProc = procedure(var Rec: PGenericRec);
DisposeRecProc = procedure(Rec: PGenericRec);
ReadRecProc = procedure(Rec: PGenericRec);
WriteRecProc = procedure(const Rec: PGenericRec);
function NewRec(ARecType: Integer): PGenericRec;
procedure DisposeRec(var Rec: PGenericRec);
procedure ReadRec(Rec: PGenericRec);
procedure WriteRec(const Rec: PGenericRec);
procedure RegisterRecType(ARecType: Integer; ANewProc: NewRecProc; ADisposeProc: DisposeRecProc; AReadproc: ReadRecFunc; AWriteProc: WriteRecProc);
implementation
type
TRecTypeReg = record
RecType: Integer;
NewProc: NewRecProc;
DisposeProc: DisposeRecProc;
ReadProc: ReadRecProc;
WriteProc: WriteRecProc;
end;
var
RecTypes: array of TRecTypeReg;
function NewRec(ARecType: Integer): PGenericRec;
var
I: Integer;
begin
Result := nil;
for I = Low(RecTypes) to High(RecTypes) do
begin
with RecTypes[I] do
begin
if RecType = ARecType then
begin
NewProc(Result);
Exit;
end;
end;
end;
raise Exception.Create('RecType not registered');
end;
procedure DisposeRec(var Rec: PGenericRec);
var
I: Integer;
begin
for I = Low(RecTypes) to High(RecTypes) do
begin
with RecTypes[I] do
begin
if RecType = Rec^.RecType then
begin
DisposeProc(Rec);
Rec := nil;
Exit;
end;
end;
end;
raise Exception.Create('RecType not registered');
end;
procedure ReadRec(var Rec: PGenericRec);
var
LRecType: Integer;
I: Integer;
begin
Rec := nil;
LRecType := ReadInteger;
for I = Low(RecTypes) to High(RecTypes) do
begin
with RecTypes[I] do
begin
if RecType = LRecType then
begin
NewProc(Rec);
try
ReadProc(Rec);
except
DisposeProc(Rec);
raise;
end;
Exit;
end;
end;
end;
raise Exception.Create('RecType not registered');
end;
procedure WriteRec(const Rec: PGenericRec);
var
I: Integer;
begin
for I = Low(RecTypes) to High(RecTypes) do
begin
with RecTypes[I] do
begin
if RecType = Rec^.RecType then
begin
WriteInteger(Rec^.RecType);
WriteProc(Rec);
Exit;
end;
end;
end;
raise Exception.Create('RecType not registered');
end;
procedure RegisterRecType(ARecType: Integer; ANewProc: NewRecProc; ADisposeProc: DisposeRecProc; AReadproc: ReadRecFunc; AWriteProc: WriteRecProc);
begin
SetLength(RecTypes, Length(RecTypes)+1);
with RecTypes[High(RecTypes)] do
begin
RecType := ARecType;
NewProc := ANewProc;
DisposeProc := ADisposeProc;
ReadProc := AReadProc;
WriteProc := AWriteProc;
end;
end;
end.
您将序列化与“通过单个
块写入
调用将所有内容写入磁盘”混为一谈。您可以序列化所需的任何内容,而不管它是从t组件
还是t持久
虽然用一个BlockWrite
调用编写所有内容一开始看起来很方便,但如果所需的记录类型要存储任何特别有趣的内容(如字符串、动态数组、接口、对象或其他基于引用或指针的类型),您很快就会发现这并不是您真正想要的
您可能还会发现变体记录不令人满意,因为您将以最低的公分母进行编码。如果不检查实际包含的类型,您将无法访问记录中的任何内容,即使是最小数据量的大小也将占用与最大数据类型相同的空间
这个问题似乎描述了多态性,因此您不妨接受该语言已经为此提供了什么。使用对象数组(或列表或任何其他容器)。然后,您可以使用虚拟方法来统一处理它们。如果需要,您可以实现记录的动态分派(例如,为每个记录提供一个函数指针,该指针指向一个知道如何处理该记录包含的数据类型的函数),但最终您可能会发现自己正在重新创建类。处理此类数据的“自然”方式是使用类,而不是记录
。无论是在定义时还是在处理实现时,使用它都会容易得多:特别是,virtual
方法非常强大,可以为特定类型的类定制流程。然后在较新版本的Delphi中使用t列表/TObjectList
或t集合
,或基于泛型的数组来存储列表
关于序列化,有几种方法。看
在您的特定情况下,困难来自您正在使用的“变体”类型的记录。主要缺点是编译器将拒绝在“variant”部分中设置任何引用计数类型的变量(例如字符串
)。因此,您将能够在这个“变量”部分中只编写“普通”变量(如integer
)。IMHO有一个很大的限制,这降低了此解决方案的兴趣
另一种可能是在定义开始时存储记录类型,例如使用RecType:integer
或更好的RecType:tenumeOptionType
,这将比数字更明确。但是,您必须手工编写大量代码,并使用指针,如果您对指针编码不是很熟练,那么很容易出错
因此,您还可以存储记录的类型信息,可通过TypeInfo(aRecordVariable)
访问。然后可以使用FillChar
初始化
type
PType1Rec = ^Type1Rec;
Type1Rec = Record
RecType: Integer;
Value: Integer;
end;
procedure NewRec1(var Rec: PGenericRec);
var
Rec1: PType1Rec;
begin
New(Rec1);
Rec1^.RecType := RecTypeForType1Rec;
Rec := PGenericRec(Rec1);
end;
procedure DisposeRec1(Rec: PGenericRec);
begin
Dispose(PType1Rec(Rec));
end;
procedure ReadRec1(Rec: PGenericRec);
begin
PType1Rec(Rec)^.Value := ReadInteger;
end;
procedure WriteRec1(const Rec: PGenericRec);
begin
WriteInteger(PType1Rec(Rec)^.Value);
end;
initialization
RegisterRecType(RecTypeForType1Rec, @NewRec1, @DisposeRec1, @ReadRec1, @WriteRec1);
type
PType2Rec = ^Type2Rec;
Type2Rec = Record
RecType: Integer;
Value: Boolean;
end;
procedure NewRec2(var Rec: PGenericRec);
var
Rec2: PType2Rec;
begin
New(Rec2);
Rec2^.RecType := RecTypeForType2Rec;
Rec := PGenericRec(Rec2);
end;
procedure DisposeRec2(Rec: PGenericRec);
begin
Dispose(PType2Rec(Rec));
end;
procedure ReadRec2(Rec: PGenericRec);
begin
PType2Rec(Rec)^.Value := ReadBoolean;
end;
procedure WriteRec2(const Rec: PGenericRec);
begin
WriteBoolean(PType2Rec(Rec)^.Value);
end;
initialization
RegisterRecType(RecTypeForType2Rec, @NewRec2, @DisposeRec2, @ReadRec2, @WriteRec2);
type
PTypeNRec = ^Type2Rec;
TypeNRec = Record
RecType: Integer;
Value: String;
end;
procedure NewRecN(var Rec: PGenericRec);
var
RecN: PTypeNRec;
begin
New(RecN);
RecN^.RecType := RecTypeForTypeNRec;
Rec := PGenericRec(RecN);
end;
procedure DisposeRecN(Rec: PGenericRec);
begin
Dispose(PTypeNRec(Rec));
end;
procedure ReadRecN(Rec: PGenericRec);
begin
PTypeNRec(Rec)^.Value := ReadString;
end;
procedure WriteRecN(const Rec: PGenericRec);
begin
WriteString(PTypeNRec(Rec)^.Value);
end;
initialization
RegisterRecType(RecTypeForTypeNRec, @NewRecN, @DisposeRecN, @ReadRecN, @WriteRecN);
var
Recs: array of PGenericRec;
procedure CreateRecs;
begin
SetLength(Recs, 3);
NewRec1(Recs[0]);
PRecType1(Recs[0])^.Value : ...;
NewRec2(Recs[1]);
PRecType2(Recs[1])^.Value : ...;
NewRecN(Recs[2]);
PRecTypeN(Recs[2])^.Value : ...;
end;
procedure DisposeRecs;
begin
for I := 0 to High(Recs) do
DisposeRec(Recs[I]);
SetLength(Recs, 0);
end;
procedure SaveRecs;
var
I: Integer;
begin
WriteInteger(Length(Recs));
for I := 0 to High(Recs) do
WriteRec(Recs[I]);
end;
procedure LoadRecs;
var
I: Integer;
begin
DisposeRecs;
SetLength(Recs, ReadInteger);
for I := 0 to High(Recs) do
ReadRec(Recs[I]);
end;
procedure RecordClear(var Dest; TypeInfo: pointer);
asm
jmp System.@FinalizeRecord
end;
function TObject.ClassType: TClass;
begin
Pointer(Result) := PPointer(Self)^;
end;