Strukturierte Typen (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Datentypen, Variablen und Konstanten - Index

Instanzen eines strukturierten Typs enthalten mehrere Werte. Zu den strukturierten Typen gehören Mengen-, Array-, Record- und Datei-, Klassen-, Klassenreferenz- und Interface-Typen. Mit Ausnahme von Mengen, die nur ordinale Werte enthalten, können strukturierte Typen auch andere strukturierte Typen beinhalten. Ein Typ kann beliebig viele strukturelle Ebenen umfassen.

Dieses Thema enthält Informationen zu folgenden strukturierten Typen:

  • Mengentypen
  • Array-Typen (statisch und dynamisch)
  • Record-Typen
  • Dateitypen

Ausrichtung von strukturierten Typen

Per Voreinstellung sind die Werte in einem strukturierten Typ zur Beschleunigung des Zugriffs an Word- oder Double-Word-Grenzen ausgerichtet.

Sie können allerdings die Byte-Ausrichtung beim Deklarieren eines strukturierten Typs durch Angabe des reservierten Wortes packed festlegen. Das Wort packed legt die Datenspeicherung in komprimierter Form fest. Dazu eine Beispieldeklaration:

 type TNumbers = packed array [1..100] of Real;

Die Verwendung von packed wird nicht empfohlen, da es die Kompatibilität mit anderen Sprachen oder Plattformen verhindern kann, den Datenzugriff verlangsamt und sich bei Zeichen-Arrays auf die Typkompatibilität auswirkt. Weitere Informationen dazu finden Sie unter Speicherverwaltung und Implizites Packen von Feldern mit einer gemeinsamen Typspezifikation.

Mengentypen

Eine Menge setzt sich aus mehreren Werten desselben ordinalen Typs zusammen. Die Werte haben keine feste Reihenfolge. Wenn ein Wert in einer Menge doppelt vorkommt, hat jedes Vorkommen dieselbe Bedeutung.

Der Bereich eines Mengentyps ist die Potenzmenge eines bestimmten Ordinaltyps, der als Basistyp bezeichnet wird. Die möglichen Werte eines Mengentyps sind Teilmengen des Basistyps, einschließlich der leeren Menge. Der Basistyp darf aus maximal 256 Werten bestehen. Die Ordinalpositionen der Werte müssen zwischen 0 und 255 liegen. Alle Konstruktionen der Form:

set of baseType

bezeichnen einen Mengentyp. Dabei ist baseType ein entsprechender ordinaler Typ.

Aufgrund der Größenbeschränkung von Basistypen werden Mengentypen normalerweise mit Teilmengen definiert. So lässt sich mit den Deklarationen:

 type
   TSomeInts = 1..250;
   TIntSet = set of TSomeInts;

ein Mengentyp mit dem Namen TIntSet erstellen, dessen Werte Integer-Zahlen im Bereich zwischen 1 und 250 sind. Sie können dazu auch folgende Anweisung verwenden:

 type TIntSet = set of 1..250;

Mit dieser Deklaration können Sie beispielsweise folgende Mengen erstellen:

 var Set1, Set2: TIntSet;
     ...
     Set1 := [1, 3, 5, 7, 9];
     Set2 := [2, 4, 6, 8, 10]

Die Konstruktion set of ... kann direkt in Variablendeklarationen verwendet werden:

 var MySet: set of 'a'..'z';
     ...
     MySet := ['a','b','c'];
Hinweis: Weitere Informationen finden Sie in der folgenden Warnmeldung: W1050 WideChar in Set-Ausdrücken auf ByteChar verkürzt (Delphi).

Hier einige weitere Beispiele für Mengentypen:

set of Byte
set of (Club, Diamond, Heart, Spade)
set of Char;

Der Operator in überprüft, ob ein Element zu einer Menge gehört:

 if 'a' in MySet then ... { do something } ;

Jeder Mengentyp kann die leere Menge enthalten, die mit [] gekennzeichnet wird. Weitere Informationen zu Mengen finden Sie in den Abschnitten "Mengen-Konstruktoren" und "Mengen-Operatoren" unter Ausdrücke (Delphi).

Array-Typen

Ein Array ist eine indizierte Menge von Elementen desselben Typs (des so genannten Basistyps). Da jedes Element einen eindeutigen Index hat, kann ein Array (im Gegensatz zu einer Menge) denselben Wert mehrmals und mit unterschiedlicher Bedeutung enthalten. Arrays können statisch oder dynamisch zugewiesen werden.

Statische Arrays

Statische Array-Typen werden mit der folgenden Konstruktion definiert:

array[indexType1, ..., indexTypen] of baseType;

indexType ist immer ein ordinaler Typ, dessen Bereich 2 GB nicht überschreitet. Da das Array über die indexTypes indiziert wird, ist die Anzahl der Elemente durch die angegebenen indexTypes beschränkt. In der Praxis sind die indexTypes normalerweise Integer-Teilbereiche.

Im einfachsten Fall eines eindimensionalen Arrays ist nur ein einziger indexType vorhanden. Zum Beispiel:

 var MyArray: array [1..100] of Char;

Hier wird eine Variable namens MyArray deklariert, die 100 Zeichenwerte enthält. Aufgrund dieser Deklaration bezeichnet MyArray[3] das dritte Zeichen in MyArray. Wenn Sie ein statisches Array erstellen und nicht allen Elementen Werte zuweisen, werden die unbenutzten Elemente dennoch vergeben. Diese Elemente enthalten zu Beginn zufällige Daten und sind mit nicht initialisierten Variablen vergleichbar.

Ein mehrdimensionales Array ist ein Array, das andere Arrays enthält. Zum Beispiel ist die Anweisung:

 type TMatrix = array[1..10] of array[1..50] of Real;

gleichbedeutend mit:

 type TMatrix = array[1..10, 1..50] of Real;

Unabhängig von der Art der Deklaration repräsentiert TMatrix immer ein Array mit 500 reellen Werten. Eine Variable namens MyMatrix vom Typ TMatrix kann auf zwei Arten indiziert werden: MyMatrix[2,45]; oder wie folgt: MyMatrix[2][45]. Genauso ist

 packed array[Boolean, 1..10, TShoeSize] of Integer;

gleichbedeutend mit:

 packed array[Boolean] of packed array[1..10] of packed array[TShoeSize] of Integer;

Die Standardfunktionen Low und High können für Array-Typbezeichner und Variablen angewendet werden. Sie liefern die untere und obere Grenze des ersten Indextyps im Array. Die Standardfunktion Length gibt die Anzahl der Elemente in der ersten Dimension des Arrays zurück.

Ein eindimensionales, gepacktes statisches Array mit Char-Werten wird als gepackter String bezeichnet. Gepackte String-Typen sind mit String-Typen und anderen gepackten String-Typen kompatibel, die dieselbe Anzahl von Elementen haben. Siehe Kompatibilität und Identität von Typen (Delphi).

Ein Array der Form array[0..x] of Char wird als nullbasiertes Zeichen-Array bezeichnet. Nullbasierte Zeichen-Arrays werden zum Speichern von nullterminierten Strings verwendet und sind mit PChar-Werten kompatibel. Siehe "Nullterminierte Strings" in String-Typen (Delphi).

Dynamische Arrays

Dynamische Arrays haben keine feste Größe oder Länge. Der Speicher für ein dynamisches Array wird reserviert, sobald Sie dem Array einen Wert zuweisen oder es an die Prozedur SetLength übergeben. Dynamische Array-Typen werden folgendermaßen deklariert:

array of Basistyp

Zum Beispiel deklariert die Anweisung:

 var MyFlexibleArray: array of Real;

ein eindimensionales Array mit reellen Elementen. Mit dieser Deklaration wird MyFlexibleArray kein Speicherplatz zugewiesen. Um ein Array im Speicher anzulegen, rufen Sie SetLength auf. Nach der vorherigen Typdeklaration weist

 SetLength(MyFlexibleArray, 20);

ein Array mit 20 reellen Zahlen und einem Index von 0 bis 19 zu. Das Aufrufen des Array-Konstruktors stellt eine alternative Methode für die Zuweisung von Speicher für dynamische Arrays dar:

 type
   TMyFlexibleArray = array of Integer;
 
 begin
   MyFlexibleArray := TMyFlexibleArray.Create(1, 2, 3 {...});
 end;

Diese Anweisung weist Speicher für drei Elemente zu und ordnet jedem Element den angegebenen Wert zu.

Ähnlich wie mit dem Array-Konstruktor kann ein dynamisches Array auch durch einen Array-Konstantenausdruck initialisiert werden (siehe das folgende Beispiel).

  procedure MyProc;
  var
    A: array of Integer;
  begin
    A := [1, 2, 3];
  end;

Im Gegensatz zu einem Array-Konstruktor kann eine Array-Konstante direkt für einen unbenannten dynamischen Array-Typ angewendet werden. Diese Syntax gilt nur für dynamische Arrays; bei anderen Array-Typen wird die Konstante möglicherweise als eine Menge interpretiert, was beim Compilieren zu dem Fehler "Inkompatible Typen" führt.

Dynamische Arrays haben immer einen ganzzahligen Index, der stets mit 0 beginnt.

Dynamische Array-Variablen sind implizit Zeiger und werden mit derselben Referenzzählung verwaltet wie lange Strings. Um ein dynamisches Array freizugeben, weisen Sie einer Variable, die das Array referenziert, den Wert nil zu, oder Sie übergeben die Variable an Finalize. Beide Methoden geben das Array unter der Voraussetzung frei, dass keine weiteren Referenzen darauf vorhanden sind. Dynamische Arrays werden immer freigegeben, sobald ihr Referenzzähler null ist. Dynamische Arrays der Länge 0 haben immer den Wert nil. Verwenden Sie für dynamische Array-Variablen nicht den Derefenzierungsoperator (^), und übergeben Sie sie auch nicht an die Prozeduren New oder Dispose.

Wenn X und Y Variablen mit demselben dynamischen Array-Typ sind, führt die Anweisung X := Y dazu, dass X auf dasselbe Array wie Y zeigt. (Es ist nicht erforderlich, vor dieser Operation Speicher für X zu reservieren.) Im Gegensatz zu Strings und statischen Arrays wird copy-on-write nicht für dynamische Arrays verwendet. Deshalb werden diese nicht automatisch kopiert, bevor einem ihrer Elemente ein Wert zugewiesen wird. Beispiel: Nach Ausführung dieses Quelltextes:

 var
   A, B: array of Integer;
   begin
     SetLength(A, 1);
     A[0] := 1;
     B := A;
     B[0] := 2;
   end;

hat A[0] den Wert 2. (Wenn A und B statische Arrays wären, hätte A[0] immer noch den Wert 1.)

Durch Zuweisungen an ein dynamisches Array über den Index (wie beispielsweise MyFlexibleArray[2] := 7) wird für das Array kein neuer Speicherplatz reserviert. Der Compiler akzeptiert auch Indizes, die außerhalb des angegebenen Bereichs liegen.

Wenn Sie im Gegensatz dazu eine unabhängige Kopie eines dynamischen Arrays erstellen möchten, müssen Sie die globale Funktion Copy verwenden:

 var
   A, B: array of Integer;
 begin
   SetLength(A, 1);
   A[0] := 1;
   B := Copy(A);
   B[0] := 2; { B[0] <> A[0] }
 end;

Bei einem Vergleich von dynamischen Array-Variablen werden nicht die Array-Werte, sondern die Referenzen verglichen. Nach Ausführung des Quelltextes:

 var
   A, B: array of Integer;
 begin
    SetLength(A, 1);
    SetLength(B, 1);
    A[0] := 2;
    B[0] := 2;
 end;

gibt A = B deshalb den Wert False zurück, aber A[0] = B[0] gibt True zurück.

Um ein dynamisches Array zu verkürzen, übergeben Sie es an SetLength oder Copy und weisen das Ergebnis wieder der Array-Variable zu. (Die Prozedur SetLength ist normalerweise schneller.) Wenn A beispielsweise ein dynamisches Array ist, können Sie mit den folgenden Anweisungen die ersten 20 Elemente von A beibehalten, und den Rest abschneiden:

 SetLength(A, 20)
 A := Copy(A, 0, 20)

Nachdem einem dynamischen Array Speicherplatz zugewiesen wurde, kann es an die Standardfunktionen Length, High und Low übergeben werden. Length liefert die Anzahl der Elemente im Array, High den höchsten Index des Arrays (Length - 1) und Low den Wert 0. Bei einem Array mit der Länge null liefert High das Ergebnis -1 (mit der unsinnigen Folge, dass High < Low ist).

Hinweis: In einigen Funktions- und Prozedurdeklarationen werden Array-Parameter in der Form array of Basistyp ohne definierten Indextyp angegeben. Beispiel: function CheckStrings(A: array of string): Boolean;

In diesem Fall kann die Funktion für alle Arrays des angegebenen Basistyps angewendet werden, unabhängig von der Größe der Arrays und der Art ihrer Indizierung. Es spielt auch keine Rolle, ob den Arrays der Speicherplatz statisch oder dynamisch zugewiesen wird.

Mehrdimensionale dynamische Arrays

Zur Deklaration von mehrdimensionalen dynamischen Arrays verwenden Sie aufeinander folgende array of ...-Konstruktionen. Zum Beispiel:

 type TMessageGrid = array of array of string;
 var Msgs: TMessageGrid;

Hier wird ein zweidimensionales String-Array deklariert. Um dieses Array zu instantiieren, rufen Sie SetLength mit zwei Integer-Argumenten auf. Wenn beispielsweise I und J Integer-Variablen sind:

 SetLength(Msgs,I,J);

wird Speicherplatz für ein I mal J großes Array zugewiesen, und Msgs[0,0] bezeichnet ein Element dieses Arrays.

Sie können auch mehrdimensionale dynamische Arrays anlegen, die nicht gleichförmig sind. Rufen Sie dazu zunächst die Funktion SetLength auf, und übergeben Sie ihr Parameter für die ersten n Dimensionen des Arrays. Zum Beispiel:

 var Ints: array of array of Integer;
 SetLength(Ints,10);

Mit dieser Anweisung weisen Sie dem Array Ints Speicherplatz für zehn Zeilen, aber für keine Spalten zu. Den Speicher für die Spalten können Sie später einzeln zuweisen (und dabei unterschiedliche Längen angeben):

 SetLength(Ints[2], 5);

Die dritte Spalte von Ints kann damit fünf Integer-Werte aufnehmen, und Sie können ihr nun Werte zuweisen (auch wenn die anderen Spalten nicht zugewiesen sind), z. B. Ints[2,4] := 6.

Im folgenden Beispiel wird mithilfe von dynamischen Arrays (und der in der Unit SysUtils deklarierten Funktion IntToStr) eine ungleichförmige String-Matrix erstellt.

 var
   A : array of array of string;
   I, J : Integer;
 begin
   SetLength(A, 10);
   for I := Low(A) to High(A) do
   begin
     SetLength(A[I], I);
     for J := Low(A[I]) to High(A[I]) do
       A[I,J] := IntToStr(I) + ',' + IntToStr(J) + ' ';
     end;
   end;

Array-Typen und Zuweisungen

Arrays sind nur dann zuweisungskompatibel, wenn sie denselben Typ haben. Da Delphi-Namensäquivalente für Typen verwendet, wird der folgende Quelltext nicht compiliert.

 var
   Int1: array[1..10] of Integer;
   Int2: array[1..10] of Integer;
       ...
   Int1 := Int2;

Damit die Zuweisung korrekt bearbeitet werden kann, deklarieren Sie die Variablen folgendermaßen:

 var Int1, Int2: array[1..10] of Integer;

oder:

 type IntArray = array[1..10] of Integer;
 var
    Int1: IntArray;
    Int2: IntArray;

String-ähnliche Operationen werden für dynamische Arrays unterstützt

Dynamische Arrays können ähnlich wie Strings bearbeitet werden. Zum Beispiel:

var
  A: array of integer;
  B: TBytes = [1,2,3,4]; //Initialization can be done from declaration
begin
  ...
  A:=[1,2,3]; // assignation using constant array
  A:=A+[4,5]; // addition - A will become [1,2,3,4,5]
  ...
end;

String-ähnliche Unterstützungsroutinen

Einige der intrinsische Routinen in Delphi unterstützen zusätzlich zu String-Operationen auch Operationen für dynamische Arrays.

System.Insert Die Funktion Insert fügt ein dynamisches Array am Positionsindex ein. Die Funktion gibt das geänderte Array zurück:

  
var
  A: array of integer;
begin
  ...
  A:=[1,2,3,4];
  Insert(5,A,2); // A will become [1,2,5,3,4]
  ...
end;

System.Delete

Die Funktion Delete löscht alle Elemente aus einem dynamischen Array und gibt das geänderte Array zurück:

  
var
  A: array of integer;
begin
  ...
  A:=[1,2,3,4];
  Delete(A,1,2); //A will become [1,4]
  ...
end;

System.Concat Mit der Funktion Concat können zwei verschiedene dynamische Arrays zusammengefügt (verkettet) werden:

  
  A := Concat([1,2,3],[4,5,6]); //A will become [1,2,3,4,5,6]

Records (traditionelle)

Ein Record (in einigen Programmiersprachen auch als Struktur bezeichnet) stellt eine heterogene Menge von Elementen dar. Die Elemente werden Felder genannt. In der Deklaration eines Record-Typs wird für jedes Feld ein Name und ein Typ festgelegt. Die Syntax für die Deklaration eines Record-Typs lautet:

 type recordTypeName = record
   fieldList1: type1;
    ...
   fieldListn: typen;
 end

recordTypeName ist ein gültiger Bezeichner, "type" gibt einen Typ an, und "fieldList" ist ein gültiger Bezeichner oder eine Liste von Bezeichnern, die durch Kommas voneinander getrennt sind. Das letzte Semikolon ist optional.

Die folgende Deklaration erstellt einen Record-Typ namens TDateRec.

 type
   TDateRec = record
     Year: Integer;
     Month: (Jan, Feb, Mar, Apr, May, Jun,
             Jul, Aug, Sep, Oct, Nov, Dec);
     Day: 1..31;
   end;

Jeder TDateRec-Record enthält drei Felder: einen Integer-Wert namens Year, einen Aufzählungswert namens Month und einen weiteren Integer-Wert zwischen 1 und 31 namens Day. Die Bezeichner Year, Month und Day sind Feldbezeichner für TDateRec und verhalten sich wie Variablen. Die Typdeklaration für TDateRec weist den Feldern Year, Month und Day aber keinen Speicherplatz zu. Die Reservierung des Speichers erfolgt erst, wenn der Record instantiiert wird:

 var Record1, Record2: TDateRec;

Diese Variablendeklaration erzeugt zwei Instanzen von TDateRec namens Record1 und Record2.

Sie können auf die Felder eines Records zugreifen, indem Sie die Feldbezeichner mit dem Record-Namen qualifizieren:

 Record1.Year := 1904;
 Record1.Month := Jun;
 Record1.Day := 16;

Alternativ dazu ist auch die Verwendung einer with-Anweisung möglich:

 with Record1 do
 begin
   Year := 1904;
   Month := Jun;
   Day := 16;
 end;

Nun können die Werte der Felder von Record1 nach Record2 kopiert werden:

 Record2 := Record1;

Da der Gültigkeitsbereich eines Feldbezeichners auf den Record beschränkt ist, in dem er sich befindet, brauchen Sie nicht auf eventuelle Namenskonflikte zwischen Feldbezeichnern und anderen Variablen zu achten.

Statt der Definition von Record-Typen kann die Konstruktion record ... auch direkt in Variablendeklarationen verwendet werden:

 var S: record
   Name: string;
   Age: Integer;
 end;

Eine solche Deklaration ist aber wenig sinnvoll, da der eigentliche Zweck eines Records darin besteht, die wiederholte Deklaration ähnlicher Variablengruppen zu vermeiden. Außerdem sind separat deklarierte Records auch dann nicht zuweisungskompatibel, wenn ihre Strukturen identisch sind.

Variante Teile in Record-Typen

Ein Record-Typ kann einen varianten Teil enthalten, der einer case-Anweisung ähnelt. Dieser variante Teil muss in der Typdeklaration nach den Feldern angegeben werden.

Mit der folgenden Syntax deklarieren Sie einen Record-Typ mit einem varianten Teil:

 type recordTypeName = record
   fieldList1: type1;
    ...
   fieldListn: typen;
 case tag: ordinalType of
   constantList1: (variant1);
    ...
   constantListn: (variantn);
 end;

Der erste Teil der Deklaration (bis zum reservierten Wort case) ist identisch mit der Deklaration eines Standard-Records. Der Rest der Deklaration (von case bis zum abschließenden optionalen Semikolon) stellt den varianten Teil dar. Im varianten Teil

  • Ist tag optional und kann ein beliebiger, gültiger Bezeichner sein. Wenn Sie tag weglassen, entfällt auch der Doppelpunkt (:).
  • Bezeichnet ordinalType einen ordinalen Typ.
  • Ist jede constantList eine Konstante (oder eine Liste von Konstanten, die durch Kommas voneinander getrennt sind), die einen Wert des Typs ordinalType bezeichnet. In den constantLists darf jeder Wert nur einmal vorkommen.
  • Ist variant eine Liste mit Deklarationen, die durch Semikolon voneinander getrennt sind. Die Liste ähnelt in etwa den fieldList: type-Konstruktionen im Hauptteil des Record-Typs. Eine Variante hat demnach folgende Form:
 fieldList1: type1;
   ...
 fieldListn: typen;

Dabei ist fieldList ein gültiger Bezeichner oder eine Liste von Bezeichnern, die durch Kommas voneinander getrennt sind. "type" ist ein Typ. Das letzte Semikolon ist optional. Bei den Typen darf es sich nicht um lange Strings, dynamische Arrays, Varianten (Typ Variant) oder Interfaces handeln. Strukturierte Typen, die lange Strings, dynamische Arrays, Varianten oder Interfaces enthalten, sind ebenfalls nicht zulässig. Zeiger auf diese Typen dürfen jedoch verwendet werden.

Die Syntax für Records mit varianten Teilen ist kompliziert, ihre Semantik ist jedoch einfach. Der variante Teil eines Records enthält mehrere Varianten, die sich denselben Speicherplatz teilen. Auf die Felder einer Variante kann jederzeit ein Lese- oder Schreibzugriff erfolgen. Wenn Sie allerdings zunächst in ein Feld einer Variante schreiben und anschließend in ein Feld einer anderen Variante, kann das zum Überschreiben der eigenen Daten führen. Falls vorhanden, funktioniert das Tag als gesondertes Feld (mit dem Typ ordinalType) im nicht varianten Teil des Records.

Variante Teile erfüllen zwei Funktionen, die sich am besten anhand eines Beispiels verdeutlichen lassen. Angenommen, Sie möchten einen Record-Typ erstellen, der Felder für unterschiedliche Daten enthält. Sie wissen, dass Sie nie alle Felder einer einzelnen Record-Instanz benötigen werden. Zum Beispiel:

 type
   TEmployee = record
   FirstName, LastName: string[40];
   BirthDate: TDate;
   case Salaried: Boolean of
     True: (AnnualSalary: Currency);
     False: (HourlyWage: Currency);
 end;

Diesem Beispiel liegt die Überlegung zugrunde, dass ein Angestellter entweder ein jährliches Festgehalt (AnnualSalary) oder einen Stundenlohn (HourlyWage) erhält, und dass für einen Angestellten immer nur eine der Zahlungsarten in Frage kommt. Wenn Sie eine Instanz von TEmployee anlegen, muss also nicht für beide Felder Speicherplatz reserviert werden. In diesem Beispiel unterscheiden sich die Varianten nur durch die Feldnamen. Die Felder könnten aber auch unterschiedliche Typen haben. Hier einige komplexere Beispiele:

 type
   TPerson = record
   FirstName, LastName: string[40];
   BirthDate: TDate;
   case Citizen: Boolean of
     True: (Birthplace: string[40]);
     False: (Country: string[20];
             EntryPort: string[20];
             EntryDate, ExitDate: TDate);
   end;
 
 type
   TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
   TFigure = record
     case TShapeList of
       Rectangle: (Height, Width: Real);
       Triangle: (Side1, Side2, Angle: Real);
       Circle: (Radius: Real);
       Ellipse, Other: ();
   end;

Der Compiler weist jeder Record-Instanz so viel Speicherplatz zu, wie die Felder in der größten Variante benötigen. Das optionale Tag und die constantLists (wie Rectangle, Triangle usw. im letzten Beispiel) spielen für die Art und Weise, wie der Compiler die Felder verwaltet, keine Rolle. Sie erleichtern lediglich die Arbeit des Programmierers.

Wie bereits erwähnt, erfüllen variante Teile noch eine zweite Aufgabe. Sie können dieselben Daten so behandeln, als würden sie zu unterschiedlichen Typen gehören. Wenn beispielsweise das erste Feld einer Variante einen 64-Bit-Real-Wert und das erste Feld einer anderen Variante einen 32-Bit-Integer-Wert enthält, können Sie dem Real-Feld einen Wert zuweisen und anschließend die ersten 32 Bit davon als Wert des Integer-Feldes auslesen (und sie beispielsweise an eine Funktion übergeben, die Integer-Parameter erwartet).

Records (erweiterte)

Zusätzlich zu den herkömmlichen Record-Typen lässt die Delphi-Sprache komplexere und "klassenähnliche" Record-Typen zu. Records können außer Feldern Eigenschaften und Methoden (einschließlich Konstruktoren), Klasseneigenschaften, Klassenmethoden, Klassenfelder und verschachtelte Typen haben. Weitere Informationen hierzu finden Sie in der Dokumentation über Klassen und Objekte (Delphi). Der folgende Beispielcode ist eine Record-Typdefinition mit einigen "klassenähnlichen" Merkmalen.

 type
   TMyRecord = record
     type
       TInnerColorType = Integer;
     var
       Red: Integer;
     class var
       Blue: Integer;
     procedure printRed();
     constructor Create(val: Integer);
     property RedProperty: TInnerColorType read Red write Red;
     class property BlueProp: TInnerColorType read Blue write Blue;
 end;
 
 constructor TMyRecord.Create(val: Integer);
 begin
   Red := val;
 end;
 
 procedure TMyRecord.printRed;
 begin
   Writeln('Red: ', Red);
 end;

Records besitzen nun einige der Merkmale von Klassen, aber gibt es wichtige Unterschiede zwischen Klassen und Records.

  • Records unterstützen keine Vererbung.
  • Records können variante Teile enthalten; Klassen nicht.
  • Records sind Wertetypen, daher werden Sie bei der Zuweisung kopiert, per Wert übergeben und dem Stack zugewiesen, wenn sie nicht als global definiert sind oder explizit mit den Funktionen New und Dispose zugewiesen werden. Klassen sind Referenztypen, daher werden Sie bei der Zuweisung nicht kopiert, per Referenz übergeben und dem Heap zugewiesen.
  • Records ermöglichen auf Win32-Plattformen das Überladen von Operatoren; Klassen ermöglichen kein Überladen von Operatoren.
  • Records werden automatisch mit einem Standardkonstruktor ohne Argumente erzeugt, Klassen dagegen müssen explizit erzeugt werden. Weil Records einen argumentlosen Standardkonstruktor haben, muss jeder benutzerdefinierte Record-Konstruktor ein oder mehrere Parameter haben.
  • Record-Typen können keine Destruktoren haben.
  • Virtuelle Methoden (die mit den Schlüsselwörtern virtual, dynamic und message angegeben werden) dürfen in Record-Typen nicht verwendet werden.
  • Im Gegensatz zu Klassen können Record-Typen auf der Win32-Plattform keine Interfaces implementieren.

Dateitypen (Win32)

Dateitypen, die auf der Win32-Plattform zur Verfügung stehen, sind Folgen von Elementen desselben Typs. Für Standard-E/A-Routinen wird der vordefinierte Typ TextFile oder Text verwendet. Dieser Typ repräsentiert eine Datei, die in Zeilen angeordnete Zeichen enthält. Weitere Informationen zur Dateieingabe und -ausgabe finden Sie unter Standardroutinen und Eingabe-Ausgabe im Abschnitt "Dateiein- und -ausgabe".

Dateitypen werden mit der folgenden Syntax deklariert:

type fileTypeName = file of type

fileTypeName ist ein gültiger Bezeichner. "type" ist ein Typ fester Länge. Zeigertypen sind weder implizit noch explizit zulässig. Dynamische Arrays, lange Strings, Klassen, Objekte, Zeiger, Varianten, andere Dateien oder strukturierte Typen, die einen dieser Typen beinhalten, können deshalb nicht in Dateien enthalten sein.

Zum Beispiel deklariert die Anweisung:

 type
    PhoneEntry = record
      FirstName, LastName: string[20];
      PhoneNumber: string[15];
      Listed: Boolean;
    end;
    PhoneList = file of PhoneEntry;

Hier wird ein Dateityp für die Aufzeichnung von Namen und Telefonnummern deklariert.

Die Konstruktion file of ... kann direkt in einer Variablendeklaration verwendet werden. Zum Beispiel:

 var List1: file of PhoneEntry;

Das Wort file selbst gibt eine untypisierte Datei an:

 var DataFile: file;

Weitere Informationen hierzu finden Sie im Abschnitt "Untypisierte Dateien" in Standardroutinen und Eingabe-Ausgabe.

Dateitypen sind in Arrays und Records nicht zulässig.

Siehe auch

Codebeispiele