uses BlockFileOfT; uses System.Runtime.InteropServices;//нужно чтоб не писать System.Runtime.InteropServices.StructLayout и т.п. uses FileArray; type DataType1 = record b: byte; constructor(b:byte) := self.b := b; end; DataType2 = record i: integer; constructor(i:integer) := self.i := i; end; DataType3 = record ch: char; constructor(ch:char) := self.ch := ch; end; DataType4 = record r: real; constructor(r:real) := self.r := r; end; DataType = ( ByteData = 1, IntData = 2, CharData = 3, RealData = 4 ); [StructLayout(LayoutKind.&Explicit)] // Позволяет явно указывать позицию каждого поля DataUnit = record [FieldOffset(0)] DataT: byte; [FieldOffset(8)] DataT1: DataType1; // У этих 4 полей одинаковая позиция [FieldOffset(8)] DataT2: DataType2; // Значит, у них будет общая память [FieldOffset(8)] DataT3: DataType3; // Но это так же значит что если записать данные 1 типа - [FieldOffset(8)] DataT4: DataType4; // данные другого типа считать не выйдет (выведет мусор) constructor(data: DataType1); begin DataT := 1; DataT1 := data; end; constructor(data: DataType2); begin DataT := 2; DataT2 := data; end; constructor(data: DataType3); begin DataT := 3; DataT3 := data; end; constructor(data: DataType4); begin DataT := 4; DataT4 := data; end; function ToString:string; override; begin var sb := new StringBuilder; sb.AppendFormat( 'DataUnit({0,8}, ', System.Enum.GetName(typeof(DataType), DataT) ); case DataType(DataT) of ByteData: sb.AppendFormat('{0})',DataT1.b); IntData : sb.AppendFormat('{0})',DataT2.i); CharData: sb.AppendFormat('{0})',DataT3.ch); RealData: sb.AppendFormat('{0})',DataT4.r); else raise new System.FormatException($'Не правильный тип данных: {DataT}'); end; Result := sb.ToString; end; end; Database = class(System.IDisposable) private const HeaderSize = 1 + 4*2; // 1 байт на версию + 4 символа (по 2 байта) на DatabaseType public version: byte; public DatabaseType: string; public BlockFile: BlockFileOf; public Data: FileArr; public constructor := exit; public constructor(fname: string; version: byte; DatabaseType: string); begin self.version := version; self.DatabaseType := DatabaseType; self.BlockFile := new BlockFileOf(fname); self.BlockFile.Offset := HeaderSize; self.BlockFile.Open(System.IO.FileMode.Create); self.Data := new FileArr(self.BlockFile); end; public class function Load(fname: string): Database; begin Result := new Database; Result.BlockFile := new BlockFileOf(fname, HeaderSize); Result.BlockFile.Reset; begin // заголовок var br := new System.IO.BinaryReader(Result.BlockFile.BaseStream); Result.version := br.ReadByte; Result.DatabaseType := string.Create(ArrGen(4,i->char(br.ReadUInt16))).TrimEnd('_'); end; Result.Data := new FileArr(Result.BlockFile); Result.Data.AutoFlush := false; Result.Data.DeleteOnExit := false; end; public procedure Save; begin begin // заголовок self.BlockFile.PosByte := 0; var bw := new System.IO.BinaryWriter(self.BlockFile.BaseStream); bw.Write(self.version); foreach var ch in self.DatabaseType.PadRight(4,'_') do bw.Write(word(ch)); end; self.Data.Flush; end; public function ToString:string; override; begin var sb := new StringBuilder; sb += $'version: {version}{#10}'; sb += $'Database Type: {DatabaseType}{#10}'; sb += $'Data:[{#10}'; BlockFile.Pos := 0; foreach var du in BlockFile.Read(BlockFile.Size) do sb += $'{#9}{du}{#10}'; sb += $']'; Result := sb.ToString; end; public procedure Dispose; begin Data.Finalize; end; end; begin var db1 := new Database('temp.bin',1,'JSBD'); // JSBD = Just Some Basic Data db1.version := 5; // Была 1, изменили на 5 db1.Data[0] := new DataUnit(new DataType1(123)); db1.Data[1] := new DataUnit(new DataType2(456)); db1.Data[2] := new DataUnit(new DataType3('A')); db1.Data[3] := new DataUnit(new DataType4(123.456)); db1.Save; db1.Dispose; db1 := Database.Load('temp.bin'); writeln(db1); writeln; // Пытаемся прочитать переменную типа integer там - где записана переменная типа real // Это, конечно, выводит мусор writeln('int from real: ', db1.Data[3].DataT2.i); end.