2010-07-11 29 views
9

Esiste un work-around per creare record di riferimento reciproco in Delphi? Ecco la versione semplificata del codice:Record di referenziamento reciproco in Delphi (Win32)

MyRec1 = record 
    arr: MyRec2Array; 
end; 

MyRec2 = record 
    mr: MyRec1; 
end; 

MyRec2Array = array of MyRec2; 

Apparentemente avanti dichiarazione di tipi di record

MyRec2 = record; 

non funziona in Delphi per Win32.

Passare alle classi anziché ai record non è positivo, perché ciò aumenterà il consumo di memoria e la complessità del codice, quindi preferirei stare con i record.

Qualche suggerimento?

+3

Eventuali duplicati: http://stackoverflow.com/questions/2420650/cross-reference-between-delphi-records –

+1

Questo non ha senso. Se supponiamo che la lunghezza di ogni MyRec2Array sia fissa e diversa da zero, stai provando a creare una struttura dati che occuperà infiniti byte ... –

+1

@Andreas Rejbrand - MyRec2Array è * dynamic * array. – Alex

risposta

14

I record sono tipi di valore, non tipi di riferimento. Ciò significa che tutti i record utilizzati come membri di una struttura dati più grande vengono posizionati in linea nella struttura stessa anziché come puntatore. Cercando di creare due record che contengono l'uno con l'altro, il compilatore verrà lanciato in un ciclo infinito mentre cercherà di capire la struttura dei record. Questo è probabilmente il motivo per cui non puoi inoltrare un record, e anche se stai cercando di inserire un tipo di riferimento (array dinamico) qui, non puoi ancora violare le regole della lingua.

Ma cosa si può fare è dichiarare un tipo puntatore-a-record come una dichiarazione anticipata, in questo modo:

PMyRec2 = ^MyRec2 
... 
MyRec2 = record 
    ... 
end; 

Naturalmente, una volta di iniziare a usare puntatori a record, è necessario preoccuparsi di assegnazione e liberando la memoria, e la complessità del codice che stavi cercando di evitare non usando le classi appare nel tuo progetto. Bottom line: fai questo con le classi. Crea uno dei record, se non entrambi, una classe. È il modo più semplice, davvero.

E l'overhead di memoria extra è trascurabile. Esce da un puntatore all'oggetto per ciascun riferimento, che è necessario per i puntatori agli oggetti, oltre a un campo nascosto (4 byte) per istanza prima di D2009 o due (8 byte) su D2009 o successivo. Questo non è affatto.

+2

+1 per menzionare le conseguenze del tipo di valore e la soluzione alternativa che utilizza i puntatori –

+0

La ricerca del puntatore è ciò che stavo cercando. Inoltre stavo pensando che ogni istanza di TObject occupa circa 30 byte. Non so da dove l'ho preso, ma dopo aver letto il tuo post, l'ho cecked in D2007 ed è davvero 4 byte. Quindi sei assolutamente corretto. Molte grazie! – Max

+0

Si noti che la limitazione del tipo di valore non si applica qui, perché il dynarray è già un tipo dinamico. Questo è probabilmente il motivo per cui la soluzione di "serg" funziona. –

4

Mentre sono completamente d'accordo con Mason, c'è un modo per aggirare la limitazione. Fondamentalmente, è possibile utilizzare un helper record per definire le funzionalità necessarie dopo che MyRec2 è stato dichiarato.

type 
    MyRec1 = record 
    arr: array of byte; 
    end; 

    MyRec2 = record 
    mr: MyRec1; 
    end; 

    MyRec1Helper = record helper for MyRec1 
    procedure AllocateMyRec2(numItems: integer); 
    function GetMyRec2(i: integer): MyRec2; 
    procedure SetMyRec2(i: integer; const value: MyRec2); 
    property Rec2[i: integer]: MyRec2 read GetMyRec2 write SetMyRec2; 
    end; 

procedure MyRec1Helper.AllocateMyRec2(numItems: integer); 
begin 
    SetLength(arr, numItems * SizeOf(myRec2)); 
end; 

function MyRec1Helper.GetMyRec2(i: integer): MyRec2; 
begin 
    Move(arr[i*SizeOf(MyRec2)], Result, SizeOf(MyRec2)); 
end; 

procedure MyRec1Helper.SetMyRec2(i: integer; const value: MyRec2); 
begin 
    Move(value, arr[i*SizeOf(MyRec2)], SizeOf(MyRec2)); 
end; 

var 
    my: MyRec2; 

begin 
    my.mr.AllocateMyRec2(2); 
    my.mr.Rec2[0].mr.AllocateMyRec2(3); 
end. 
4

La domanda si presenta come uno scherzo per me - non si tratta di riferimenti reciproci, si tratta di un ciclo infinito di tipo definizione. Per quanto riguarda i riferimenti reciproci, che possano essere risolti definendo i tipi all'interno di un record (io ho usato Delphi 2009):

type 
    MyRec2 = record 
    type 
    MyRec2Array = array of MyRec2; 
    type 
    MyRec1 = record 
     arr: MyRec2Array; 
    end; 
    var 
    mr: MyRec1; 
    end; 

Ma come utilizzare il tipo di record di cui sopra? Molto divertente. :)

procedure TForm1.Button1Click(Sender: TObject); 
var 
    R: MyRec2; 

begin 
    SetLength(R.mr.arr, 1); 
// R.mr.arr[0]:= ???; 
end; 
Problemi correlati