Delphi &引用;降序;德尔福唱片公司?

Delphi &引用;降序;德尔福唱片公司?,delphi,records,Delphi,Records,我知道你不能从记录中得出任何东西,但我不知道如何用一句话来总结我的问题。如果需要,请编辑标题 我想在这里做的是创建一个泛型类型的数组,它可以是X个类型中的一个,数组将填充这些自定义类型(它们有不同的字段,这很重要)。简单的方法是只创建一个变量记录数组,每个变量都有自己的类型,但显然不能像这样重新声明标识符: GenericRec = Record case SubTypeName: TSubTypeName of type1name: (SubRec: Type1); typ

我知道你不能从记录中得出任何东西,但我不知道如何用一句话来总结我的问题。如果需要,请编辑标题

我想在这里做的是创建一个泛型类型的数组,它可以是X个类型中的一个,数组将填充这些自定义类型(它们有不同的字段,这很重要)。简单的方法是只创建一个变量记录数组,每个变量都有自己的类型,但显然不能像这样重新声明标识符:

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;