delphi record tlist

Delphi TList de registros



record (6)

Acabamos de encontrarnos con un problema similar aquí con una lista genérica de registros. Espero que el siguiente código psuedo ayude.

type PPat = ^TPat; TPat = record data: integer; end; ... var AList: TList<PPat>; ... procedure TForm1.Button1Click(Sender: TObject); var obj: PPat; begin obj := AList[0]; obj.data := 1; Assert(obj.data = AList[0].data); // correct end; procedure TForm1.FormCreate(Sender: TObject); var obj: PPat; begin AList := TList<PPat>.Create; GetMem(obj, SizeOf(TPat)); // not shown but need to FreeMem when items are removed from the list obj.data := 2; AList.Add(obj); end;

Necesito almacenar una lista temporal de registros y estaba pensando que un TList sería una buena forma de hacerlo. Sin embargo, no estoy seguro de cómo hacer esto con un TList y me preguntaba si esto es lo mejor y si alguien tiene algún ejemplo de cómo hacerlo.


La forma más fácil es crear tu propio descendiente de TList . Aquí hay una aplicación de consola de muestra rápida para demostrar:

program Project1; {$APPTYPE CONSOLE} uses SysUtils, Classes; type PMyRec=^TMyRec; TMyRec=record Value: Integer; AByte: Byte; end; TMyRecList=class(TList) private function Get(Index: Integer): PMyRec; public destructor Destroy; override; function Add(Value: PMyRec): Integer; property Items[Index: Integer]: PMyRec read Get; default; end; { TMyRecList } function TMyRecList.Add(Value: PMyRec): Integer; begin Result := inherited Add(Value); end; destructor TMyRecList.Destroy; var i: Integer; begin for i := 0 to Count - 1 do FreeMem(Items[i]); inherited; end; function TMyRecList.Get(Index: Integer): PMyRec; begin Result := PMyRec(inherited Get(Index)); end; var MyRecList: TMyRecList; MyRec: PMyRec; tmp: Integer; begin MyRecList := TMyRecList.Create; for tmp := 0 to 9 do begin GetMem(MyRec, SizeOf(TMyRec)); MyRec.Value := tmp; MyRec.AByte := Byte(tmp); MyRecList.Add(MyRec); end; for tmp := 0 to MyRecList.Count - 1 do Writeln(''Value: '', MyRecList[tmp].Value, '' AByte: '', MyRecList[tmp].AByte); WriteLn('' Press Enter to free the list''); ReadLn; MyRecList.Free; end.

Esto elimina un par de cosas:

  • Se encarga de liberar la memoria.
  • No tiene que encasillar todo para usarlo.

Como dijeron Remy y Warren, es un poco más de trabajo porque tienes que asignar la memoria cuando agregas nuevos registros.


Primero, si quiere combinar un TList clásico con Registros, necesitará:

  1. Asigna tus registros en el montón, no en la pila. Usa GetMem como lo hizo Remy.
  2. Tome la dirección del registro y agréguelo al TList.
  3. Al eliminar un elemento de la lista y usarlo, desreferenciarlo:
  4. Recuerde liberar y limpiar, después.

Combinar Listas con Registros requiere tanto trabajo de "punteros y heap-management" que tal técnica solo estaría dentro de las capacidades de un experto.

Las alternativas a lo que ha solicitado que todavía usan algo llamado "TList" incluyen el uso de un estilo genist.collections TList, con tipos de registros, que tendrían todos los beneficios de TList, pero requeriría básicamente hacer un montón de copias de registro para obtener datos en él.

Las formas más idiomáticas de Delphi para hacer lo que pides son:

  1. use un TList o TObjectList con un tipo de clase en lugar de un registro. Normalmente terminas subclasando TList o TObjectList en este caso.

  2. Utilice una matriz dinámica de tipos de registros, pero tenga en cuenta que es más difícil ordenar un tipo de matriz, y que expandir un tipo de matriz en tiempo de ejecución no es tan rápido como lo es con un TList.

  3. Usa genéricos. Colecciones TList con tus clases. Esto le permite evitar la subclasificación de TList o TObjectList cada vez que quiera utilizar una lista con una clase diferente.

Un ejemplo de código que muestra matrices dinámicas:

TMyRec = record /// end; TMyRecArray = array of TMyRec; procedure Demo; var myRecArray:TMyRecArray; begin SetLength(myRecArray,10); end;

Ahora para obtener información general sobre por qué TList no es fácil de usar con los tipos de registros:

TList es más adecuado para su uso con tipos de clase, porque una variable de tipo ''TMyClass'', donde ''tipo TMyClass = clase .... fin;'' puede "referirse a" fácilmente como un valor de puntero, que es lo que TList tiene.

Las variables de tipo Record son Value-Types en Delphi, mientras que los valores de clase son implícitamente valores de referencia. Puede pensar en valores de referencia por referencia como punteros sigilosos. No tiene que desreferenciarlos para obtener sus contenidos, pero cuando los agrega a un TList, en realidad solo está agregando un puntero al TList, no haciendo una copia ni asignando ninguna memoria nueva.

La respuesta de Remy te muestra literalmente cómo hacer exactamente lo que quieres, y solo escribo mi respuesta porque quiero advertirte sobre los detalles de lo que estás preguntando, y sugiero que consideres alternativas también.


Puede usar TList para eso, por ejemplo:

type pRec = ^sRec; sRec = record Value: Integer; ... end; var List: TList; Rec: pRec; I: Integer; begin List := TList.Create; try for I := 1 to 5 do begin GetMem(Rec); try Rec^.Value := ...; ... List.Add(Rec); except FreeMem(Rec); raise; end; end; ... for I := 0 to List.Count-1 do begin Rec := pRec(List[I]); ... end; ... for I := 0 to List.Count-1 do FreeMem(pRec(List[I])); List.Clear; finally List.Free; end; end;


Puedes echar un vistazo a nuestra envoltura TDynArray . Se define en una unidad de código abierto, que funciona desde Delphi 6 hasta XE.

Con TDynArray , puede acceder a cualquier matriz dinámica (como TIntegerDynArray = array of integer o TRecordDynArray = array of TMyRecord ) usando propiedades y métodos tipo TList , por ejemplo Count, Add, Insert, Delete, Clear, IndexOf, Find, Sort y algunos nuevos métodos como LoadFromStream, SaveToStream, LoadFrom y SaveTo que permiten la serialización binaria rápida de cualquier matriz dinámica, incluso que contenga cadenas o registros; un método CreateOrderedIndex también está disponible para crear índices individuales de acuerdo con el contenido de la matriz dinámica. También puede serializar el contenido de la matriz en JSON, si lo desea. Slice, Reverse métodos Slice, Reverse o Copy también están disponibles.

Manejará una matriz dinámica de registros, e incluso registros dentro de registros, con cadenas u otras matrices dinámicas dentro.

Al usar una variable de Count externa, puede acelerar mucho la adición de elementos en la matriz dinámica referida.

type TPerson = packed record sCountry: string; sFullName: string; sAddress: string; sCity: string; sEmployer: string; end; TPersons = array of TPerson; var MyPeople: TPersons; (...) procedure SavePeopleToStream(Stream: TMemoryStream); var aPeople: TPerson; aDynArray: TDynArray; begin aDynArray.Init(TypeInfo(TPersons),MyPeople); aPeople.sCountry := ''France''; aPeople.sEmployer := ''Republique''; aDynArray.Add(aPeople); aDynArray.SaveToStream(Stream); end; // no try..finally Free needed here

También hay una clase TDynArrayHashed , que permite el hashing interno de un contenido de matriz dinámico. Es muy rápido y es capaz de cifrar cualquier tipo de datos (hay hashers estándar para cadenas, pero puede suministrar los suyos, incluso la función hash se puede personalizar).

Tenga en cuenta que TDynArray y TDynArrayHashed son solo envoltorios alrededor de una variable de matriz dinámica existente. Por lo tanto, puede inicializar un wrapper TDynArray cuando lo necesite, para acceder de manera más eficiente a cualquier matriz dinámica nativa de Delphi.


Todo depende del tipo de datos que desee almacenar.

Puede considerar usar TCollection y TCollectionItem .

Aquí está el código ( editado ) de una unidad de trabajo, en el que utilicé TCollection para leer una lista de definiciones de informes de una carpeta. Cada informe consistía en una especie de plantilla y una declaración SQL que debía almacenarse junto con un nombre de archivo.

Como está editado y usa algunas de mis unidades (TedlFolderRtns lee archivos en una lista interna, por nombrar solo uno), el ejemplo es lo suficientemente simple como para ser útil. Con unos pocos reemplaza todo, puedes adaptarte a tus necesidades.

Busque TCollection en la ayuda, puede hacer mucho con eso. Y mantiene el manejo de su código muy bien agrupado en una estructura de clase.

unit cReports; interface uses SysUtils, Classes, XMLDoc, XMLIntf, Variants, // dlib - Edelcom eIntList, eProgSettings,eFolder ; type TReportDefItem = class(TCollectionItem) private fSql: string; fSkeleton: string; fFileName: string; procedure Load; procedure SetFileName(const Value: string); public constructor Create(Collection:TCollection); override; destructor Destroy ; override; property FileName: string read fFileName write SetFileName; property Sql : string read fSql write fSql; property Skeleton : string read fSkeleton write fSkeleton; end; TReportDefList = class(TCollection) private function OsReportFolder: string; function GetAction(const Index: integer): TReportDefItem; public constructor Create(ItemClass: TCollectionItemClass); destructor Destroy; override; procedure LoadList; function Add : TReportDefItem; property Action [ const Index:integer ]: TReportDefItem read GetAction; end; implementation { TReportDefList } constructor TReportDefList.Create(ItemClass: TCollectionItemClass); begin inherited; end; destructor TReportDefList.Destroy; begin inherited; end; function TReportDefList.Add: TReportDefItem; begin Result := TReportDefItem( Add() ); end; function TReportDefList.GetAction(const Index: integer): TReportDefItem; begin if (Index >= 0) and (Index < Count) then Result := TReportDefItem( Items[Index] ) else Result := Nil; end; procedure TReportDefList.LoadList; var Folder : TedlFolderRtns; i : integer; Itm : TReportDefItem; begin Folder := TedlFolderRtns.Create; try Folder.FileList( OsReportFolder,''*.sw.xml'', False); for i := 0 to Folder.ResultListCount -1 do begin Itm := Add(); Itm.FileName := Folder.ResultList[i]; end; finally FreeAndNil(Folder); end; end; function TReportDefList.OsReportFolder: string; begin Result := Application.ExeName + ''_RprtDef''; end; { TReportDefItem } constructor TReportDefItem.Create(Collection: TCollection); begin inherited; fSql := ''''; fSkeleton := ''''; end; destructor TReportDefItem.Destroy; begin inherited; end; procedure TReportDefItem.Load; var XMLDoc : IXMLDocument; TopNode : IXMLNode; FileNode : IXmlNode; iWebIndex, iRemoteIndex : integer; sWebVersion, sRemoteVersion: string; sWebFileName: string; begin if not FileExists(fFileName ) then Exit; XMLDoc := TXMLDocument.Create(nil); try XMLDoc.LoadFromFile( fFileName ); XMLDoc.Active := True; TopNode := XMLDoc.ChildNodes.FindNode(''sw-report-def''); if not Assigned(TopNode) then Exit; FileNode := TopNode.ChildNodes.First; while Assigned(FileNode) do begin fSql := VarToStr( FileNode.Attributes[''sql''] ); fSkeleton := VarToStr( FileNode.Attributes[''skeleton''] ); FileNode := FileNode.NextSibling; end; XMLDoc.Active := False; finally XMLDoc := Nil; end; end; procedure TReportDefItem.SetFileName(const Value: string); begin if fFileName <> Value then begin fFileName := Value; Load; end; end; end.

Usar como :

fReports := TReportDefList.Create( TReportDefItem ); fReports.LoadList();