![]() |
Dynamisches Array als Klasse
Hallo,
Hier habe ich eine Dynamic-Array Klasse erstellt, inspiriert durch meine Veröffentlichung vor längerer Zeit hier in der DP unter dem Titel "Dynamische Arrays auch unter Delphi 3" Damals hatte ich für jeden Arraytyp eine separate Klasse erstellt. Heute ist nur noch eine einzige Klasse davon übrig geblieben als eindimensionales Array. Alle anderen Klassentypen habe ich entfernt. Die vorliegende Klasse TGenericDynArray kann Integer-Typen und Pointer speichern. Mit dem Pointer sollte es möglich sein, auch kompliziertere Datenstrukturen in einem solchen Array zu speichern. Leider finde ich den damaligen Thread nicht mehr, weshalb ich nun einen neuen eröffne. Außerdem weiß ich von Lazarus, das dort dynamische Arrays auch als Klasse(n) definiert sind. Ich hatte zuerst auch an mehrdimensionale Arrays gedacht, daher auch die auf mehrdimensionale Arrays ausgelegte SetLength() Methode. Da jedoch in heutigen Delphi- und Lazarus Versionen dynamische Arrays beliebiger Dimensionalität eh Standard sind, belasse ich es nun bei dem eindimensionalen Array. Hier nun die Unit:
Delphi-Quellcode:
Ihr dürft diese Unit per copy & paste in Eure Projekte zu jedem beliebigen Zweck übernehmen.
unit DynArray;
interface uses Sysutils, Classes; type PByte = ^Byte; PShortInt = ^ShortInt; PSmallInt = ^SmallInt; PWord = ^Word; PInteger = ^Integer; PLongint = ^Longint; //Workaround für 1 Dimensionales Array TElementsType = ( etByte, etShortInt, etSmallInt, etWord, etInteger, etLongint, etPointer ); TGenericDynArray = class(TObject) private FCount: Longint; FDims: Longint; FSize: Longint; //FItem: Longint; FType: TElementsType; FArray: TList; FVector: TStringList; function GetItems(Index: Integer): Longint; function GetItemP(Index: Integer): Pointer; function GetItemType: TElementsType; procedure SetItems(Index: Integer; value: Longint); procedure SetItemP(Index: Integer; value: Pointer); protected constructor Create; //Für spätere Verwendung public //Dieser Constructor instantiiert das Array Objekt constructor CreateArray(Elements: Integer; ElType: TElementsType); destructor Destroy; override; property Count: Longint read FCount; //Anzahl Elemente -> gesamtes Array property Dims: Longint read FDims; //Anzahl Dimensionen property Size: Longint read FSize; //Größe eines Elementes //die Array Elemente property Items[Index: Integer]: Longint read GetItems write SetItems; default; //passt dann auf alle Typen //Wenn Pointer, dann passt dieser Item besser //Aber der LOngint Item wandelt Longint intern in Pointer property ItemP[Index: Integer]: Pointer read GetItemP write SetItemP; //Hier kann der Datentyp der Elemente gelesen werden //Festgelegt wird er im Konstruktor mit dem 2. Parameter property ItemType: TElementsType read GetItemType; procedure SetLength(ElCount: Longint); end; T1DimArray = TGenericDynArray; //Fügt sich besser in die Namenskonvention ein function NewByte(b: Byte): PByte; function NewShortint(si: ShortInt): PShortInt; function NewSmallInt(si: SmallInt): PSmallInt; function NewWord(w: Word): PWord; function NewInteger(i: Integer): PInteger; function NewLongint(i: Longint): PLongint; procedure DisposeShortint(var P: PShortint); procedure DisposeByte(var P: PByte); procedure DisposeSmallint(var P: PSmallint); procedure DisposeWord(var P: PWord); procedure DisposeInteger(var P: PInteger); procedure DisposeLongint(var P: PLongint); procedure InitArray(var A: TList; n: Integer; eltype: TElementsType); procedure CutArray(var A: TList; n: Integer); implementation { für Dimensionsänderungen während der Laufzeit konzipiert } { kann natürlich überall benutzt werden, wo man diese Funktionalität braucht } { Das Array selber ist mit TList realisiert und behält deshalb die Daten, die } procedure InitArray(var A: TList; n: Integer; eltype: TElementsType); begin while A.Count < n do case eltype of etByte : A.Add(NewByte(0)); etShortint : A.Add(NewShortint(0)); etSmallint : A.Add(NewSmallint(0)); etWord : A.Add(NewWord(0)); etInteger : A.Add(NewInteger(0)); etLongint : A.Add(NewLongint(0)); etPointer : A.Add(nil); end; end; { für Dimensionsänderungen während der Laufzeit konzipiert } { Das Array selber ist mit TList realisiert und behält deshalb die Daten, die } { nicht abgeschnitten werden, automatisch } procedure CutArray(var A: TList; n: Integer); begin while n < A.Count do A.Delete(A.Count-1); end; function NewByte(b: Byte): PByte; var P: PByte; begin GetMem(P, Sizeof(b)); P^ := b; Result := P; end; function NewShortint(si: ShortInt): PShortInt; var P: PShortint; begin GetMem(P, Sizeof(si)); P^ := si; Result := P; end; function NewSmallInt(si: SmallInt): PSmallInt; var P: PSmallint; begin GetMem(P, Sizeof(si)); P^:= si; Result := P; end; function NewWord(w: Word): PWord; var P: PWord; begin GetMem(P, Sizeof(w)); P^:= w; Result := P; end; function NewInteger(i: Integer): PInteger; var P: PInteger; begin GetMem(P , Sizeof(i)); P^:= i; Result := P; end; function NewLongint(i: Longint): PLongint; var P: PLongint; begin GetMem(P , Sizeof(i)); P^:= i; Result := P; end; procedure DisposeShortint(var P: PShortint); begin freemem(P, Sizeof(Shortint)); end; procedure DisposeByte(var P: PByte); begin freemem(P, Sizeof(Byte)); end; procedure DisposeSmallint(var P: PSmallint); begin freemem(P, Sizeof(SmallInt)); end; procedure DisposeWord(var P: PWord); begin freemem(P, Sizeof(Word)); end; procedure DisposeInteger(var P: PInteger); begin freemem(P, Sizeof(Integer)); end; procedure DisposeLongint(var P: PLongint); begin freemem(P, Sizeof(Longint)); end; { TGenericDynArray } function TGenericDynArray.GetItems(Index: Integer): Longint; begin Result:=0; case FType of etByte : Result := Byte(FArray.Items[Index]^) and $ff; etShortInt : Result := Shortint(FArray.Items[Index]^) and $ff; etSmallInt : Result := Smallint(FArray.Items[Index]^) and $ffff; etWord : Result := Word(FArray.Items[Index]^) and $ffff; etInteger : Result := Integer(FArray.Items[Index]^); etLongint : Result := LongInt(FArray.Items[Index]^); etPointer : Result := Longint(FArray.Items[Index]); end; end; function TGenericDynArray.GetItemP(Index: Integer): Pointer; begin result:=nil; if FType = etPointer then Result := FArray.Items[Index]; end; function TGenericDynArray.GetItemType: TElementsType; begin Result := FType; end; procedure TGenericDynArray.SetItems(Index: Integer; value: Longint); var b: Byte; begin case FType of etByte: begin b := value and $ff; FArray.Items[Index] := NewByte(b); end; etShortInt: begin FArray.Items[Index] := NewShortint(value and $ff); end; etSmallInt: begin FArray.Items[Index] := NewSmallInt(value and $ffff); end; etWord: begin FArray.Items[Index] := NewWord(value and $ffff); end; etInteger: FArray.Items[Index] := NewInteger(value); etLongint: FArray.Items[Index] := NewLongint(value); etPointer: FArray.Items[Index] := Pointer(value); end; end; procedure TGenericDynArray.SetItemP(Index: Integer; value: Pointer); begin SetItems(Index, Longint(value)); end; constructor TGenericDynArray.Create; //für zukünftige Verwendung begin //nicht zum Instanziieren inherited Create; FArray := TList.Create; end; constructor TGenericDynArray.CreateArray(Elements: Integer; ElType: TElementsType); var i: Integer; P: Pointer; begin p:=nil; inherited Create; FArray := TList.Create; for i := 0 to Elements-1 do case ElType of etByte : begin FArray.Add(NewByte(0)); FSize := Sizeof(Byte); end; etShortInt: begin FArray.Add(NewShortint(0)); FSize := Sizeof(Shortint); end; etSmallInt: begin FArray.Add(NewSmallInt(0)); FSize := Sizeof(Smallint); end; etWord : begin FArray.Add(NewWord(0)); FSize := Sizeof(Word); end; etInteger : begin FArray.Add(NewInteger(0)); FSize := Sizeof(Integer); end; etLongint : begin FArray.Add(NewLongint(0)); FSize := Sizeof(Longint); end; etPointer : begin FArray.Add(P); FSize := Sizeof(Pointer); end; end; FCount := FArray.Count; FDims := 1; end; destructor TGenericDynArray.Destroy; begin FArray.Free; inherited Destroy; end; procedure TGenericDynArray.SetLength(ElCount: Longint); var delta,ix,switch: Integer; Dims: array[0..1] of Longint; begin Dims[0] := ElCount; //weil vorher Dims: array of Longint, statt ElCount if High(Dims)<2 then //Hier wurde die Anzahl aktueller Dimensionen geprüft begin //wer mag, kann auf mehrdimensionale Arrays erweitern ix := 0; //wie ursprünglich vorgesehen, doch aus technischen delta := Dims[0]-FArray.Count; //Gründen nicht weiter verfolgt. if delta < 0 then switch := -1 else //Differenz zwichen neuer und alter Anzahl Array-Elemente if delta > 0 then switch := +1 else switch := 0; //um übersichtlicheren Code zu erhalten case switch of -1: if Dims[0]>=0 then while ix > delta do { oder while FArray.Count > delta } begin //oder CutArray(FArray, Dims[0]); FArray.Delete(FArray.Count-1); dec(ix); end; 0: ; //Keine Aktion +1: while FArray.Count < Dims[0] do //oder InitArray(FArray, Dims[0], FType); begin case FType of //wenn neue Länge (Elementanzahl) größer als vorherige etByte: FArray.Add(NewByte(0));//dann neue Elemente dazu und mit NULL initialisieren etShortint: FArray.Add(NewShortint(0)); etSmallint: FArray.Add(NewSmallint(0)); etInteger : FArray.Add(NewInteger (0)); etLongint : FArray.Add(NewLongint (0)); etPointer : FArray.Add(nil); end; { von case Ftype } end; { von case +1 -> while do begin } end; { von case } end; { von if High(Dims) } FCount := FArray.Count; end; end. Soeben hat mir der User "Delphi Laie" den Link zu jenem Thread wieder beschafft. Leider kann ich dort nichts mehr korrigieren. Ich will jedoch den Link zu jenem Thread wenigstens hier setzen, damit die anderen Überlegungen von eventuellen Interessenten auch gefunden werden. ![]() Schließlich sind dynamische Arrays unter Lazarus auch als Klassen(n) implementiert. |
AW: Dynamisches Array als Klasse
Hallo,
der Aufwand dafür ist ja recht gross. Wäre es nicht einfacher TObjectList (unit Contnrs) dafür zu verwenden? |
AW: Dynamisches Array als Klasse
Zitat:
![]() |
AW: Dynamisches Array als Klasse
Zitat:
|
AW: Dynamisches Array als Klasse
Zitat:
Die Tlist erhält ja auch keine Objektzeiger, sondern Zeiger auf Variablen des mit ElType vereinbarten Datentyps. Ich hätte höchstens gleich nur Longint oder Pointer zulassen können, da intern der Zeiger in der Liste immer mit 4 Byte Länge gespeichert wird, auch dann, wenn ich nur Byte Werte speichern will. Diese Bytes passen auch in eine Longint Speicherstelle. Ich hätte höchstens statt der TList ein Array[0..0] of Byte verwenden können, wie in der ersten Version, siehe Link im ersten Beitrag. Dann hätte ich mit Setlength immer die aktuell benötigte Bytezahl festgelegt. Deshalb hatte ich ja in der ersten Version (siehe Link) auch für jeden unterstützten Datentyp eine eigene Klasse. |
AW: Dynamisches Array als Klasse
Wäre es aber nicht etwas "einfacher", das alte Programm von D3 wenigstens auf D7 upzudaten und dann die nativen dynamischen Arrrays zu verwenden,
also jetzt nochmal mit dem Ersatz für diese Arrays rumzuspielen? |
AW: Dynamisches Array als Klasse
Konnte man unter D3 noch nicht einen Integer auf Pointer casten und umgekehrt? Falls ja kannst du die das ganze New/ Disposezeugs schenken?
|
AW: Dynamisches Array als Klasse
Die Unit scheint sich noch weiter vereinfachen zu lassen: Die case-Unterscheidungen zu den verschiedenen Integertypen ließ ich weg und benutze in jedem Falle nur das für die Pointer, also das, was hinter
etPointer: steht, lasse ich ausführen. Das funktioniert auch, und zwar für alle Integer-Typen. Abgeleitete Klassen funktionieren auch für Arrays anderen Typs (auch records), sofern der Speicherbedarf konstant ist. Bei Strings funktioniert es bisher leider nur bei solchen mit konstanter Länge (string[x]). |
AW: Dynamisches Array als Klasse
@himitsu: Wäre es nicht, denn ich der alten Version mußte ich Anpassungen vornehmen, um mehrdimensionale Arrays unterstützen zu können, danach funktionierten zwar die 2- und 3 dimensionalen Arrays, die eindimensionalen aber aus mir unerklärlichen Gründen nicht mehr. Außerdem glaube ich nicht, das per Suchen und Ersetzen einfach die Array Zugriffe geändert werden müssen und gut is, dann ließe sich das tatsächlich updaten. Aber dann mmuss ja auf jeden Fall auch die SetLength Methode in allen Klassen angepasst werden.
@Bjoerk: Das konnte man. Ja. Ok, so ließe sich das TGenericDynArray sicher noch vereinfachen. @Delphi Laie: Ja das sollte klappen. Meine Intention aber war auch, mit der direkten Zuweisung von Integer Werten arbeiten zu können. Du aber hast Bjoerk's Vorschlag aufgegriffen und nun nur noch die Zuweisung an den Pointer drin. Habe es noch nicht getestet, aber es kann sein, das @Bjoerk Recht hat. Dann könnte ein Longint auch da zugewiesen werden, mit:
Delphi-Quellcode:
Habe ich jetzt noch nicht getestet, aber wenn das geht, dann hat Bjoerk Recht.
var
myArray: TGenericDynArray; I: Longint; begin myArray := TGenericDynArray.CreateArray(5{,ElType würde nicht mehr gebraucht}); I := 100; myArray.ItemsP[2] := Pointer(I); end. |
AW: Dynamisches Array als Klasse
Liste der Anhänge anzeigen (Anzahl: 1)
Die Aufspaltung in mehrere Integertypen halte ich insofern für entbehlich, als daß die Größenbeschränkung der Integervariablen, wie überhaupt alle Beschränkungen des Computers i.d.R. eher restriktiv und hinderlich sind. Der Pointer agiert wie der größte Integertyp (Integer, der bis 2^31-1 ohne Vorzeichenumschaltung funktioniert) und umfaßt damit gewissermaßen alle Integertypen.
Solang man bei Arrayelementen mit gleichem bzw. konstantem Speicherbedarf bleibt, funktioniert die Unit bestens. Auch ein Record of Integer (benötige ich in meinem ![]() Erstellt man typabhängig eigene Klassen (dabei kann man sich die Klassenableitung zunutze machen) und man definiert nur die eine typabhängige "property" als "default", entfällt die Adressierung ".Items" der einzelnen Arrayelemente, außerdem entfällt der ^-Operator. Diese Datenstruktur, auch wenn sie auf TList basiert, ist tatsächlich wie ein (dynamisches!) Array benutzbar. Was Delphi 3 recht ist, ist Delphi 2 billig. Im Anhang ist mein Delphi-2-Projekt, das die Verwendung der verschiedenen Datentypen demonstrieren soll. Es wundert mich, daß bisher noch keine Mosereinwände á la "Was soll das, Delphi kann das ab Version 4.0 von Hause aus" kamen (Edit: Doch, himitsu äußerte sich). Mich interessierte das, und delphi2004 beschäftigte sich auf meine vorsichtige Anfrage noch einmal damit. Nunmehr funktioniert es für eindimensionale Arrays (mit der genannten Einschränkung) perfekt, und dafür danke ich ihm auch an dieser Stelle noch einmal herzlich! Nicht zuletzt lernt man ja auch bei solchen Experimentalprojekten hinzu. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:30 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz