redefines

Datensatzvariable zum erneuten Definieren einer anderen Variablen deklarieren

Sie können eine Datensatzvariable deklarieren, die einen Speicherbereich erneut definiert, der bereits einer anderen Datensatzvariablen zugeordnet ist. So können Sie beispielsweise eine Schleife schreiben, die nacheinander die Datenbereiche aus einer seriellen Datei liest, auch wenn die Struktur der abgerufenen Daten bei jedem Datenabruf anders ist, wie in folgendem Beispiel:
Record RecordA type SerialRecord
   { fileName = "myFile"  }
   record_type char(1);
   field1 char(20); 
end 

Record RecordB type BasicRecord
   10 record_type char(1);
   10 field2 bigint;
   10 field3 decimal(7);
   10 field4 char(8); 
end

Program ProgramX type basicProgram
   myRecordA RecordA;
   myRecordB RecordB {redefines = myRecordA};
   
   function main();
     get next myRecordA;
     while (myRecordA not endOfFile)
       if (myRecordA.record_type == "A")
         myFunction01(myRecordA.field1);
       else
         myFunction02(myRecordB.field2, myRecordB.field3, myRecordB.field4);
       end
       get next myRecordA;
     end
   end 
end
Innerhalb der Schleife führt die Funktion die folgenden Aktionen aus:
  1. Überprüfung des ersten Feldes im Eingabedatensatz auf einen Code, der die Struktur der verbleibenden Daten angibt.
  2. Verarbeitung der anderen Felder in den abgerufenen Daten anhand des Eingabedatensatzes oder eines zweiten Basisdatensatzes. Der zweite Datensatz bezieht sich auf denselben Speicherbereich wie der Eingabedatensatz, ist jedoch anders strukturiert.

Um eine Datensatzvariable als Neudefinition einer anderen Datensatzvariablen zu deklarieren, müssen Sie die Eigenschaft 'redefines' verwenden. Die Eigenschaft akzeptiert den Namen einer anderen Datensatzvariablen. Diese Eigenschaft ist nur in einer Datensatzvariablendeklaration verfügbar, nicht jedoch in einer Datensatzabschnittsdefinition.

Der ursprüngliche Datensatz und der überschreibende Datensatz können einen beliebigen Typ eines strukturierten Datensatzes aufweisen, wobei folgende Einschränkungen gelten:
  • Die Datensätze müssen sich in demselben Gültigkeitsbereich befinden. Wenn Sie beispielsweise den ersten Datensatz als Feld eines als Container dienenden Datensatzes deklarieren, müssen Sie auch den Datensatz, der den ersten Datensatz erneut definiert, als Feld in demselben Container-Datensatz deklarieren:
    Record StructuredRecordA
       10 x INT;
    end
    
    Record StructuredRecordB
      10 y INT;
    end
    
    Record TestRecord
      myRec1 StructuredRecordA;
      myRec2 StructuredRecordB { redefines=myRec1};
    end
    Ebenso gilt: Wenn Sie einen Datensatz in einer Bibliothek, aber außerhalb einer Funktion deklarieren, müssen Sie auch den zweiten Datensatz in derselben Bibliothek und ebenfalls außerhalb einer Funktion deklarieren.
  • Der überschreibende Datensatz muss entweder dieselbe Länge haben wie der ursprüngliche Datensatz oder kürzer sein. Diese Einschränkung verhindert, dass Ihr Code auf einen Bereich in dem überschreibenden Datensatz zugreift, der außerhalb des erneut definierten Speicherbereichs liegt.
  • Für bestimmte Datensatzstereotypen sind unter Umständen verdeckte Byte erforderlich. Informationen hierzu finden Sie in der Dokumentation für den betreffenden Datensatztyp.

Die Eigenschaften, die Sie dem ursprünglichen Datensatz zuordnen, haben keine Auswirkungen auf den überschreibenden Datensatz. Es wird lediglich der Speicherbereich erneut definiert.

Es kann kein überschreibender Datensatz für Ein-/Ausgabe verwendet werden.

Feld zum erneuten Definieren eines anderen Felds in demselben Datensatz deklarieren

Sie können die Eigenschaft 'redefines' nur dann auf ein Feld anwenden, wenn die folgenden Bedingungen erfüllt sind:
  • Das Feld für die Neudefinition und das ursprüngliche, neu definierte Feld müssen sich in demselben Datensatz befinden.
  • Der Datensatz muss strukturiert sein.
  • Beide Felder müssen sich auf derselben Ebene befinden und dasselbe übergeordnete Element haben.
  • Die Länge des Felds für die Neudefinition darf die Länge des ursprünglichen, neu definierten Felds nicht überschreiten.
Die beiden Felder können unterschiedliche Typen aufweisen. Hierbei wird jedoch nur der Typ des erneut definierten Felds beibehalten, wenn Sie den Datensatz übergeben oder initialisieren. Das folgende Beispiel veranschaulicht dieses Konzept:
Record exampleRecord1
   10 a INT;
      20 aa INT;
      20 ab CHAR(4) { redefines = a.aa };
   10 b CHAR(4) { redefines = a };
end

Wenn Sie eine Variable auf der Basis von exampleRecord1 in einer Aufrufanweisung übergeben, werden die Daten des erneut definierten Bereichs so konvertiert, als würde es sich bei ihnen um INT und nicht um CHAR(4) handeln. Wenn Sie a.ab initialisieren (beispielsweise mithilfe der Anweisung 'set empty'), werden die Daten des erneut definierten Bereichs auf binäre Nullen und nicht auf Leerzeichen gesetzt.

Das Feld für die Neudefinition muss unmittelbar auf das erneut definierte Feld folgen, ohne Berücksichtigung von untergeordneten Feldern und Neudefinitionen. Die folgenden beiden Beispiele sind gültig:
Record exampleRecord2
   10 a int;
      20 x int;
   10 b char(4) { redefines = a };
      20 z char(4);
   10 c char(4) { redefines = a };
end
Felder, die als Feldgruppe definiert sind, können nicht erneut definiert werden. Wenn ein Feld eine Feldgruppe ist, weil eines seiner übergeordneten Felder eine Feldgruppe ist, kann es jedoch erneut definiert werden.
Record exampleRecord3
   10 a int[2];
   10 b char(2) { redefines = a };  // Ungültig, da 'a' eine Feldgruppe ist
end

Record exampleRecord4
   10 a bigint;
   10 b char(1)[8] { redefines = a };  // Gültig
end

Record exampleRecord5
   10 top char(4)[3];
      20 a int;
      20 b smallint { redefines = a };  // Gültig, obwohl 'a' eine Feldgruppe ist
end

Record exampleRecord6
   10 top char(12)[5];
      20 middle char(4)[3];
         30 a int;
         30 b smallint { redefines = a };  // Gültig, obwohl 'a' eine Feldgruppe ist
end
Das nachstehende ausführliche Beispiel verwendet Neudefinitionen von Feldern, um US-amerikanische Datumsangaben (der Monat ist das erste Element) mit europäischen Datumsangaben (der Tag ist das erste Element) zu normalisieren. In der Datensatzdefinition RecC sind usBillingDate und euroBillingDate unter billingDate eingerückt, obwohl alle drei Elemente dieselbe Ebenennummer (10) haben. Die Einrückung ist ein visueller Hinweis, um deutlich zu machen, dass alle drei Felder denselben Speicherbereich belegen, obwohl sie dieselben Ebenennummern haben. Das Feld für die Neudefinition überschreibt immer das erneut definierte Feld.
package com.companyb.customer;

Record RecC type basicRecord
   10 dateFormat SMALLINT;
   10 billingDate CHAR(8);
      10 usBillingDate CHAR(8) {redefines=billingDate};
         20 month CHAR(2);
         20 day CHAR(2);
         20 year CHAR(4);   
      10 euroBillingDate CHAR(8) {redefines=billingDate};
         20 day CHAR(2);
         20 month CHAR(2);
         20 year CHAR(4);      
   10 deliveryDate CHAR(8);
      10 usDeliveryDate CHAR(8) {redefines=deliveryDate};
         20 month CHAR(2);
         20 day CHAR(2);
         20 year CHAR(4);      
      10 euroDeliveryDate CHAR(8) {redefines=deliveryDate};
         20 day CHAR(2);
         20 month CHAR(2);
         20 year CHAR(4);      
End


program RedefinesExample type BasicProgram ( 
   inDateFormat SMALLINT,
   inDeliveryMonth CHAR(2),
   inDeliveryDay CHAR(2),
   inDeliveryYear CHAR(4),
   inBillingMonth CHAR(2),
   inBillingDay CHAR(2),
   inBillingYear CHAR(4))
   {alias="REDXMP3"}

   // 'aRec' ist ein strukturierter Datensatz, dessen Elemente 'billingDate' (Fakturierungsdatum) und 'deliveryDate' (Lieferdatum)
   // jeweils durch zwei andere Elemente mit Unterstruktur erneut definiert werden,
   // deren Ebene und übergeordnetes Element identisch sind.   
   aRec RecC;
   
   const USDateFormat SMALLINT=1;
   const EURODateFormat SMALLINT=2;
   
   function main()
      aRec.dateFormat = inDateFormat;
      // Wenn US-amerikanisches Datumsformat verwendet werden soll
      if ( inDateFormat == USDateFormat )
         usBillingDate.month = inBillingMonth;
         usBillingDate.day = inBillingDay;
         usBillingDate.year = inBillingYear;
         usDeliveryDate.month = inDeliveryMonth;
         usDeliveryDate.day = inDeliveryDay;
         usDeliveryDate.year = inDeliveryYear;
      else // Datum muss im europäischen Format dargestellt werden
         euroBillingDate.month = inBillingMonth;
         euroBillingDate.day = inBillingDay;
         euroBillingDate.year = inBillingYear;
         euroDeliveryDate.month = inDeliveryMonth;
         euroDeliveryDate.day = inDeliveryDay;
         euroDeliveryDate.year = inDeliveryYear;
      end
   end
   
end

Feedback