Samengestelde X++-gegevenstypen

Note

Community-belangengroepen zijn nu verplaatst van Yammer naar Microsoft Viva Engage. Als u wilt deelnemen aan een Viva Engage-community en deel wilt nemen aan de meest recente discussies, vult u het formulier Toegang aanvragen tot Finance and Operations Viva Engage Community in en kiest u de community waaraan u wilt deelnemen.

In dit artikel worden samengestelde gegevenstypen in X++beschreven. De samengestelde gegevenstypen in X++ zijn matrices, containers, klassen als gegevenstypen, gedelegeerden als gegevenstypen en tabellen als gegevenstypen.

Array

Een matrix is een variabele die een lijst bevat met items met hetzelfde gegevenstype. De elementen van een matrix worden geopend met behulp van gehele getallen. U gebruikt een afzonderlijke instructie om elk element in een matrix te initialiseren. Wanneer u een containergegevenstype of een matrixobject gebruikt om een verzameling te maken, kunt u meerdere elementen initialiseren met behulp van één instructie. Standaard hebben alle items in een matrix de standaardwaarde van het gegevenstype in de matrix. Er zijn drie soorten matrices: dynamische matrices, matrices met vaste lengte en deels op schijfmatrices.

  • Dynamische matrices: deze matrices worden gedeclareerd met behulp van een lege matrixoptie. Met andere woorden, ze hebben alleen vierkante haken ([]).
  • Matrices met vaste lengte: deze matrices kunnen het aantal items bevatten dat is opgegeven in de declaratie. Matrices met vaste lengte worden gedeclareerd als dynamische matrices, maar er wordt een lengteoptie opgenomen in de haken.
  • Gedeeltelijk op schijfmatrices: deze matrices worden gedeclareerd als dynamische matrices of matrices met een vaste lengte die aangeven hoeveel items in het geheugen moeten worden bewaard. De andere items worden opgeslagen op schijf en worden automatisch geladen wanneer ernaar wordt verwezen.

X++ ondersteunt slechts ééndimensionale matrices. U kunt echter het gedrag van meerdere matrixindexen nabootsen. (Zie de sectie Meerdere matrixindexen voor meer informatie). Variabelen in objecten en tabellen kunnen worden gedeclareerd als matrices. Deze functionaliteit wordt bijvoorbeeld gebruikt in adresregels in de standaardtoepassing. Met een matrixverzamelingsklasse kunt u objecten opslaan in een matrix.

Matrixindexen beginnen bij 1. Naar het eerste item in de matrix wordt verwezen als [1], het tweede item wordt verwezen als [2], enzovoort. De volgende syntaxis wordt gebruikt voor toegang tot een matrixelement: ArrayItemReference = ArrayVariable [ Index ]. In deze syntaxis is ArrayVariable de id van de matrix en index het nummer van het matrixelement. Index kan een expressie met een geheel getal zijn. Item nul [0] wordt gebruikt om de matrix te wissen. Als een waarde is toegewezen aan index 0 in een matrix, worden alle elementen in de matrix opnieuw ingesteld op de standaardwaarde.

Een toewijzing van één hele matrix aan een andere wordt uitgevoerd met verwijzing.

Matrixvoorbeelden

public void ArrayMethod()
{
    int myArray[10]; // Fixed-length array with 10 integers.
    myArray[4] = 1; // Accessing the 4th element in the array.
    myArray[0] = 0; // Resets all elements in intArray.

    // Dynamic array of integers.
    int intArray[];

    // Dynamic array of variables of type Datatype.
    //Datatype arrayVariable[];

    // Fixed-length arrays.
    boolean boolArray[100]; // Fixed-length array of booleans with 100 items.

    // Two examples of Partly On Disk Arrays.
    // Dynamic integer array with only 100 elements in memory.
    int arrayVariable [ ,100];
    // Fixed-length string array with 1000 elements, and only 200 in memory.
    str arrayVariable2 [1000,200];

    // A dynamic array of integers.
    int i[];
    // A fixed-length real array with 100 elements.
    real r[100];
    // A dynamic array of dates with only 10 elements in memory.
    date d[,10];
    // A fixed length array of NoYes variables with 100 elements and 10 in memory.
    NoYes ny[100,10];
}

Meerdere matrixindexen

In sommige talen, zoals C++ en C#, kunt u matrices declareren met meer dan één index. Met andere woorden, u kunt 'matrices van matrices' definiëren. In X++kunt u niet rechtstreeks meerdere matrixindexen maken, omdat er slechts ééndimensionale matrices worden ondersteund. U kunt echter meerdere indexen implementeren met behulp van de methode die in deze sectie wordt beschreven. U wilt bijvoorbeeld een matrix declareren die twee dimensies heeft, om een bedrag te bewaren dat per land/regio per dimensie wordt verdiend. Er zijn 10 landen/regio's en drie dimensies. In C++ en C# declareert u de volgende matrix.

// This is C# or C++ code, not X++ code.
long earning[10, 3];

X++ biedt echter geen ondersteuning voor deze declaratie. In plaats daarvan kunt u een eendimensionale matrix definiëren waarbij het aantal elementen het product is van de elementen in elke dimensie. Hier volgt een voorbeeld.

public void MultipleArrayMethod()
{
    // Step 1: define a one-dimensional array with the number
    // of elements that is the product of the elements in each dimension.
    real earnings[10*3];

    // Step 2: to refer to a specific element, such as earnings[i,j], write the following:
    // declare i and j (maybe) and assign the value to something
    int i = 1;
    int j = 2;
    real element = earnings[(i-1)*3 + j];
}

// This can be written into a macro like this:
#localmacro.earningIndex
(%1-1)*3+%2
#endmacro

public void CallTheMacro()
{
    // Next, call the specific element within the macro like this:
    int i = 1;
    int j = 2;
    real element = earnings[#earningIndex(i,j)];

    // The previous scheme can be extended to any number of dimensions.
    // The element a[i1, i2, ..., ik] can be accessed by computing the
    // offset into an array containing (d1*d2*...*dk) elements.
    //(i1 - 1)*d2*d3*..*dk +
    //(i2 - 1)*d3*d4*...*dk + .... +
    //(ik-1 -1)*dk +
    //(ik-1)
}

Container

Een containerobject is een dynamische lijst met items die primitieve gegevenstypen of samengestelde gegevenstypen bevatten. Een container is handig wanneer u verschillende typen waarden moet doorgeven tussen de client- en serverlagen. Als u echter van plan bent om herhaaldelijk toe te voegen aan een lijst in een lus, is een container geen goede keuze. Containers zijn het meest geschikt voor processen die geen overmatige aanpassing van de grootte of inhoud van de container inhouden. Wanneer een container overmatige toevoegingen van gegevens ondergaat, kunnen de algehele systeemprestaties afnemen omdat containergegevens herhaaldelijk moeten worden gekopieerd en nieuwe ruimte herhaaldelijk moet worden toegewezen.

Een container is geen klasse. Een container bevat een geordende reeks primitieve waarden of andere containers. Vanwege de flexibiliteit van anytype biedt een container een goede manier om waarden van verschillende typen samen op te slaan. Een container kan worden opgeslagen in de database. Een container is een van de kolomtypen die u kunt selecteren wanneer u Application Explorer gebruikt om een kolom toe te voegen aan een tabel. Een container lijkt enigszins op een matrix of verzamelingen, zoals de klassen Lijst of Stack . U kunt echter nooit de grootte of inhoud van een container wijzigen nadat de container is gemaakt.

X++-instructies die een container lijken te wijzigen, zijn intern bezig met het bouwen van een nieuwe container en het kopiëren van waarden, indien nodig. Zelfs een toewijzing van een container aan een andere containervariabele maakt een nieuwe kopie van de container. Al deze bewerkingen kunnen van invloed zijn op de prestaties. In de functies die toegang bieden tot een container (zoals conPeek), is de container 1 gebaseerd, niet op basis van 0. Indexering is gebaseerd op 1 voor matrices. De standaardwaarde van een container is leeg. De container bevat geen waarden. Sommige instructies die gebruikmaken van containers lijken een container te wijzigen, maar binnen het systeem worden containers nooit gewijzigd. In plaats daarvan worden de gegevens uit de oorspronkelijke container gecombineerd met gegevens uit de opdracht om een nieuwe container te bouwen. U kunt een nieuwe container maken met behulp van een van de volgende functies: conDel, conIns of conPoke.

Daarnaast heeft de globale klasse statische methoden voor het verwerken van containers. Deze methoden omvatten con2ArraySource, con2Buf, con2List, con2Str, containerFromXmlNode, conView en str2Con. Er zijn verschillende intrinsieke functies voor het verwerken van een container, zoals conIns en conPeek. De functie X++ conPeek retourneert een willekeurig type , daarom is het gemakkelijker om de waarden uit een container te lezen wanneer u het type van elke waarde niet kent. Een anytype kan worden toegewezen aan elk X++-waardetype, mits de waarde kan worden geconverteerd. Uw code is gemakkelijker te lezen wanneer expliciete conversies van gegevenstypen worden vermeden. Wijs daarom waarden uit een container toe aan hetzelfde gegevenstype dat is gebruikt om de waarde in de container te plaatsen. U mag geen container toewijzen aan een willekeurig type omdat het systeem mogelijk niet in staat is om de juiste conversies te bepalen. In dergelijke gevallen kunnen onverwacht gedrag of fouten optreden.

Container vergelijken met andere opties

Het containertype lijkt op andere constructies, zoals matrices en verzamelingsklassen, zoals Lijst en Stack. Het verschil tussen een container en List is dat een exemplaar van de lijstklasse veranderlijk is. Een lijstobject wijst eerst meer ruimte toe dan de bijbehorende gegevens verbruiken. Wanneer er vervolgens gegevens worden toegevoegd, wordt de ruimte opgevuld. Dit gedrag is efficiënter dan steeds meer ruimte toewijzen wanneer een element wordt toegevoegd. Een update van een lijst voert sneller uit dan vergelijkbare bewerkingen in een container.

Wanneer u een lijstobject maakt, bepaalt u het ene type gegevens dat door het lijstobject kan worden opgeslagen. Deze beperking is minder flexibel voor een lijst dan voor een container. U kunt echter objecten opslaan in een lijst, terwijl in een container alleen waardetypen kunnen worden opgeslagen. Het verschil tussen een container en een matrix is dat een matrix alleen items van het gedeclareerde type kan bevatten. U kunt geheugenruimte toewijzen voor een matrix en die ruimte later vullen met waarden. U kunt bijvoorbeeld waarden in een lus invullen. Dit gedrag is efficiënt en presteert goed. Wanneer u een nieuwe container wilt bouwen door nieuwe gegevens toe te voegen, kunt u de += operator of de conIns-functie gebruiken. De += operator is het snellere alternatief. Gebruik de functie conIns alleen als u nieuwe gegevens wilt toevoegen vóór de laatste index van de oorspronkelijke gegevens.

U kunt objectverwijzingen niet opslaan in containers. Wanneer de compiler een poging detecteert om een objectverwijzing op te slaan in een container, wordt er een foutbericht weergegeven. Als het type element dat aan de container wordt toegevoegd een willekeurig type is, kan de compiler niet bepalen of de waarde een verwijzingstype is. In dit geval staat de compiler de poging toe. Hoewel de compiler de code niet als onjuist diagnosticeert, wordt er tijdens runtime een fout gegenereerd.

Containervoorbeelden

public void ContainerExample()
{
    // First, declare the variables you are using.
    container myContainer;
    container myContainer4;
    container myContainer5;
    // Three ways to declare a container.
    myContainer = [1];
    myContainer += [2];
    myContainer4 = myContainer5;

    // Declare a container.
    container cr3;

    // Assign a literal container to a container variable.
    cr3 = [22, "blue"];

    // Declare and assign a container.
    container cr2 = [1, "blue", true];

    // Mimic container modification (implicitly creates a copy).
    cr3 += [16, strMyColorString];
    cr3 = conIns(cr3, 1, 3.14);
    cr3 = conPoke(cr3, 2, "violet");

    // Assignment of a container (implicitly creates a copy).
    cr2 = cr3;

    // Read a value from the container.
    str  myStr = conPeek(cr2, 1);

    // One statement that does multiple assignments from a container.
    str myStr;
    int myInt;
    container cr4 = ["Hello", 22, 20\07\1988];
    [myStr, myInt] = cr4; // "Hello", 22

    // Example of applying the = operator to a container. The example
    // initializes myContainer2 and myContainer33.
    myContainer2 = [2, "apple"];

    // Next, you make a copy of myContainer33 and assign the copy to myContainer2.
    myContainer33 = [33, "grape"];
    myContainer2 = myContainer33;  // The container that myContainer2 had been holding is no longer available and cannot be recovered.
    // An example of building a new container by
    // assigning a new value to myContainer33 through the += operator.
    myContainer33 += [34, "banana"];
}

// Container example. In this example, variable2 and variable33 hold different containers.
static void JobC(Args _args)
{
    container variable2, variable33;
    variable2 += [98];
    variable33 = variable2;
    variable2 += [97];
}

// List class example. In this example, variable2 and variable33 refer to the same List object.
static void JobL(Args _args)
{
    List variable2,variable33;
    variable2 = new List(Types::Integer);
    variable2.addEnd(98);
    variable33 = variable2;
    variable2.addEnd(97);
}

// The automatic type conversion by anytype also applies to the special syntax for making multiple
// assignments from a container in one statement. This is shown in the following code example,
// which assigns a str to an int, and an int to a str.
static void JobContainerMultiAssignmentUsesAnytype(Args _args)
{
    container con2;
    int int4;
    str str7;
    con2 = ["11", 222];
    [int4, str7] = con2;
    info(strfmt("int4==11==(%1), str7==222==(%2)", int4, str7));
}

/***  Output:
Message (10:36:22 am)
int4==11==(11), str7==222==(222)
***/

static void UseQuery()
{
    // An example of how the compiler diagnoses attempts to store object in containers
    container c = [new Query()];   // This statement will cause the error message shown below.
    /*** Instance of type 'Query' cannot be added to a container. ***/

    // An example of a code that won't cause an error message, but will
    // cause an error message to be thrown at runtime.
    anytype a = new Query();
    container d = [a];
}

Klassen als gegevenstypen

Een klasse is een typedefinitie die zowel variabelen als methoden voor exemplaren van de klasse beschrijft. (De exemplaren van een klasse worden ook wel objecten genoemd.) Een klasse is alleen een definitie voor objecten en alle objecten zijn null wanneer ze worden gedeclareerd. In Application Explorer is elke toepassingsklasse onder het knooppunt Klassen een gegevenstype. U kunt variabelen van deze typen in uw code declareren. U kunt exemplaren van een klasse maken en de exemplaren toewijzen aan variabelen.

Klassen kunnen worden genest in broncode. Geneste klassen zijn alleen beschikbaar in formulieren (zoals een klasse die FormRun uitbreidt) en worden gebruikt om besturingselementen, gegevensbronnen of gegevensvelden weer te geven. Een kenmerkdecoratie, zoals de kenmerkversiering op een klasse of methode, kan het achtervoegsel van de kenmerknaam weglaten als het achtervoegsel Kenmerk is. Daarom staat X++ [MyFavorite] toe in plaats van [MyFavoriteAttribute] te vereisen. Daarnaast worden kenmerken nu toegepast op de handlers van gemachtigden en methoden om de handlers toe te wijzen aan deze doelen.

In AX 2012 en eerdere versies kunt u een methode aanwijzen die moet worden uitgevoerd op de client of op de server. In financiële en operationele toepassingen wordt alle gecompileerde X++-code echter uitgevoerd als .NET Common Intermediate Language (CIL) op de server. Er is geen code meer die wordt geëvalueerd op de clientsite of in de browser. Daarom worden de trefwoorden van de client en de server nu genegeerd. Hoewel deze trefwoorden geen compileerfout veroorzaken als ze worden gebruikt, moeten ze niet worden gebruikt in nieuwe code.

Privé- en beveiligde lidvariabelen

Voorheen werden alle lidvariabelen die in een klasse zijn gedefinieerd, beveiligd. U kunt nu de zichtbaarheid van lidvariabelen expliciet maken door de persoonlijke, beveiligde en openbare trefwoorden toe te voegen. De interpretatie van deze modifiers is duidelijk en is afgestemd op de semantiek voor methoden:

  • private : de lidvariabele kan alleen worden gebruikt binnen de klasse waarin deze is gedefinieerd.
  • beveiligd : de lidvariabele kan worden gebruikt in de klasse waarin deze is gedefinieerd en alle subklassen van die klasse.
  • public : de lidvariabele kan overal worden gebruikt. Het is zichtbaar buiten de grenzen van de klassehiërarchie waar deze is gedefinieerd.

Standaard worden lidvariabelen die niet zijn voorzien van een expliciete wijzigingsfunctie, nog steeds beveiligd. Als best practice moet u echter expliciet de zichtbaarheid opgeven. Zoals we eerder hebben beschreven, kunt u, wanneer een lidvariabele is gedefinieerd als openbaar, worden geopend buiten de klasse waarin deze is gedefinieerd. In dit geval moet u een kwalificatie opgeven waarmee het object wordt aangeduid dat als host fungeert voor de variabele. Als u de kwalificatie wilt opgeven, gebruikt u de punt-notatie, net als voor methode-aanroepen.

In het volgende voorbeeld wordt veld1 geopend met behulp van de expliciete deze kwalificatie. In dit geval is het misschien geen goed idee om een lidvariabele openbaar te maken, omdat die benadering de interne werking van de klasse blootstelt aan de consumenten en daarom een sterke afhankelijkheid creëert tussen de klasse-implementatie en de consumenten. U moet altijd proberen alleen afhankelijk te zijn van een contract, niet van een implementatie.

public class AnotherClass3
{
    int field1;
    str field2;
    void new()
    {
        this.field1 = 1;   // Explicit object designated.
        field2 = "Banana";  // 'this' assumed, as usual.
    }
}

Statische constructors en statische velden

Statische velden zijn velden die worden gedeclareerd met behulp van het statische trefwoord. Conceptueel zijn statische velden van toepassing op de klasse, niet op exemplaren van de klasse. Statische constructors worden gegarandeerd uitgevoerd voordat statische aanroepen of exemplaar-aanroepen naar de klasse worden uitgevoerd. De uitvoering van de statische constructor is relatief ten opzichte van de sessie van de gebruiker. U roept de statische constructor nooit expliciet aan. In plaats daarvan genereert de compiler code om ervoor te zorgen dat de constructor precies één keer wordt aangeroepen, voordat een andere methode in de klasse wordt aangeroepen. Een statische constructor wordt gebruikt om statische gegevens te initialiseren of een actie uit te voeren die slechts één keer moet worden uitgevoerd. U kunt geen parameters opgeven voor de statische constructor en deze moet worden gemarkeerd met het statische trefwoord.

// An example of how a singleton (call instance in the example below)
// can be created using the static constructor.
public class Singleton
{
    private static Singleton instance;
    private void new()
    {
    }
    static void TypeNew()    // This is the static constructor.
    {
        instance = new Singleton();
    }

    public static Singleton Instance()
    {
        return Singleton::instance;
    }
}

// The singleton ensures that only one instance of the class
// will be called, which is consumed by the following.
{
    // Your code here.
    Singleton i = Singleton::Instance();
}

Klasse-elementen in Application Explorer

Onder de meeste klasseknooppunten in Application Explorer zijn er twee speciale knooppunten: een classDeclaration-knooppunt en een nieuw knooppunt. Een classDeclaration bevat altijd het trefwoord X++- klasse . Aanvullende trefwoorden, zoals uitbreidingen, kunnen worden opgenomen om de klasse te wijzigen. Dit knooppunt kan ook declaraties van lidvariabelen bevatten.

In het volgende voorbeeld zijn de variabelen m_priority en m_rectangle lid van de klasse.

// An example of a classDeclaration.
public class YourDerivedClass extends YourBaseClass
{
    int m_priority;
    Rectangle m_rectangle;
    void new(int _length, int _width)
    {
        this.m_rectangle = new Rectangle(_length, _width);
    }
}

Een nieuwe operator bevat logica die wordt uitgevoerd wanneer de nieuwe operator wordt gebruikt om een exemplaar van de klasse te maken. De logica in de nieuwe methode kan een object maken en dat object toewijzen aan een variabele die is gedeclareerd in de classDeclaration. Elke klasse kan slechts één nieuwe methode hebben. In de nieuwe methode moet u echter vaak de nieuwe methode van de basisklasse aanroepen. Als u de nieuwe methode van de basisklasse wilt aanroepen, roept u super()aan.

In het volgende voorbeeld ziet u de nieuwe methode voor de klasse YourDerivedClass in het vorige voorbeeld van classDeclaration . In deze nieuwe methode maakt de code een exemplaar van de klasse Rechthoek . Het exemplaar wordt toegewezen aan de variabele m_rectangle . Het trefwoord dat in het voorbeeld wordt gebruikt, is optioneel, maar als u het opneemt, kan IntelliSense nuttiger zijn.

// An example of the new method from the previous classDeclaration example.
void new(int _length, int _width)
{
    this.m_rectangle = new Rectangle(_length, _width);
}

Geheugenbeheer

Uiteindelijk tijdens runtime hebben de meeste objecten geen variabele meer die ernaar verwijst. Het systeem scant op deze objecten en wist ze uit het geheugen. De geheugenruimte wordt vervolgens beschikbaar voor ander gebruik. De objectklasse heeft een methode met de naam Finalize. De uiteindelijke methode is echter geen destructor. De runtime roept nooit de uiteindelijke methode aan, zelfs wanneer een object wordt verzameld als garbage.

Systeemklassen

In Application Explorer, onder Systeemdocumentatieklassen>, is er een lijst met de kernelklassen of systeemklassen. Systeemklassen zijn niet geschreven in X++, en u kunt hun broncode niet zien. U kunt geen systeemklassen toevoegen. Systeemklassen hebben meestal een nieuwe methode, maar ze hebben geen classDeclaration-knooppunt . Elke toepassingsklasse breidt impliciet de objectsysteemklasse uit. Sommige systeemklassen worden uitgebreid door een toepassingsklasse met een vergelijkbare naam. XClassFactory wordt bijvoorbeeld uitgebreid door ClassFactory. In dergelijke gevallen moet u de systeemklasse niet gebruiken. Zie 'Toepassingsklassen vervangen voor systeemklassen' in klassen en methoden voor meer informatie.

Extensiemethoden

Met de extensiemethodefunctie kunt u extensiemethoden toevoegen aan een doelklasse door de methoden in een afzonderlijke extensieklasse te schrijven. De volgende regels zijn van toepassing:

  • De extensieklasse moet statisch zijn.
  • De naam van de extensieklasse moet eindigen op het achtervoegsel van tien tekens _Extension, maar er is geen beperking voor het deel van de naam die voorafgaat aan het achtervoegsel.
  • Elke extensiemethode in de extensieklasse moet als openbaar statisch worden gedeclareerd.
  • De eerste parameter in elke extensiemethode is het type dat de extensiemethode uitbreidt. Wanneer de extensiemethode wordt aangeroepen, mag de aanroeper echter niets doorgeven voor de eerste parameter. In plaats daarvan geeft het systeem automatisch het vereiste object door voor de eerste parameter.
  • Het doel van een extensiemethode moet een klasse, tabel, weergave of toepassingsobjecttype zijn.

Een extensieklasse kan statische privé- of beveiligde methoden bevatten. Deze methoden worden doorgaans gebruikt voor implementatiedetails en worden niet weergegeven als extensies. De methodetechniek voor extensies heeft geen invloed op de broncode van de klasse die deze uitbreidt, waardoor de toevoeging aan de klasse geen overlaag vereist.

Upgrades naar de doelklasse worden nooit beïnvloed door bestaande uitbreidingsmethoden. Als een upgrade naar de doelklasse een methode met dezelfde naam als de extensiemethode toevoegt, kan de extensiemethode niet meer worden bereikt via objecten van de doelklasse. De methodemethode voor extensies maakt gebruik van dezelfde door puntscheidingstekens gescheiden syntaxis die u vaak gebruikt om reguliere exemplaarmethoden aan te roepen. Extensiemethoden hebben toegang tot alle openbare artefacten van de doelklasse, maar ze hebben geen toegang tot alles wat is beveiligd of privé is. Daarom kunnen uitbreidingsmethoden worden beschouwd als een soort syntactische suiker. Ongeacht het doeltype wordt een extensieklasse gebruikt om extensiemethoden aan het type toe te voegen. Een extensietabel wordt bijvoorbeeld niet gebruikt om methoden aan een tabel toe te voegen en er is geen uitbreidingstabel.

// An example of an extension class holding a few extension methods.
public static class AtlInventLocation_Extension
{
    public static InventLocation refillEnabled(
        InventLocation _warehouse,
        boolean _isRefillEnabled = true)
    {
        _warehouse.ReqRefill = _isRefillEnabled;
        return _warehouse;
    }

    public static InventLocation save(InventLocation _warehouse)
    {
        _warehouse.write();
        return _warehouse;
    }
}

Gemachtigden als gegevenstypen

Een gemachtigde verzamelt methoden die zich hierop abonneren. De gedelegeerde geeft de parameterhandtekening op die alle abonneemethoden moeten delen. Wanneer de gemachtigde wordt aangeroepen, roept de gemachtigde elk van de abonnees aan. Een gedelegeerde retourneert nooit een waarde en kan geen standaardwaarde hebben. In eerste instantie heeft elke gemachtigde geen geabonneerde methoden. Er is geen limiet voor het aantal parameters dat een gemachtigde kan declareren en er is geen beperking voor het type van deze parameters. De gemachtigde is altijd leeg, omdat het enige doel van de gedelegeerde is om het contract te definiëren waaraan abonnees moeten voldoen. Een gemachtigde hoeft niet te worden gedefinieerd in een klasse. Gedelegeerden kunnen ook worden gedefinieerd in een tabel, formulier of query.

Voorbeelden van gemachtigden

abstract class VarDatClass
{

    void new(utcdatetime _dateTime, str _changeDescription)
    {
        // An example of subscribing a delegate.
        this.notifyChanged += eventhandler(this.InfologChanges);
        this.notifyChanged += eventhandler(this.SaveInDatabase);
        
        notifyChange(_dateTime, _changeDescription);
    }

    void notifyChange(utcdatetime _dateTime, str _changeDescription)
    {
        // An example of calling a delegate.
        this.notifyChanged(_dateTime, _changeDescription);
    }
    
    // delegate method examples
    // An example of declaring a delegate.
    delegate void notifyChanged(utcdatetime _dateTime, str _changeDescription)
    {
    }

    // method that is to be subscribed.
    public static void InfologChanges(utcDateTime _dateTime, str _changeDescription)
    {
        info("A notification has occurred calling static handler:" +
            DateTimeUtil::toStr(_dateTime) +
            " Message:" +
            _changeDescription);
    }
    
    // method that is to be subscribed.
    public static void SaveInDatabase(utcDateTime _dateTime, str _changeDescription)
    {
       // save changes in database.
    }
    
    
}

Tabellen als gegevenstypen

Alle tabellen kunnen worden behandeld als klassedefinities. Een tabelvariabele kan worden beschouwd als een exemplaar (object) van de tabeldefinitie (klasse). Voor elk veld in een tabelvariabele is de standaardwaarde leeg. U kunt velden adressen en methoden voor tabellen maken. De methoden kunnen worden aangeroepen op exemplaren van de tabel. Als u records in tabellen wilt bewerken (dat wil zeggen, lezen, bijwerken, invoegen en verwijderen), moet u ten minste één tabelvariabele declareren die de record in de focus kan houden. Als best practice moet u de naam van de tabel gebruiken als de naam van de variabele, maar een eerste kleine letter gebruiken. Hier volgen enkele belangrijke verschillen tussen tabellen en objecten:

  • U kunt geen ruimte toewijzen voor tabelvariabelen. Toewijzing wordt impliciet uitgevoerd.
  • Velden in tabelvariabelen zijn openbaar. U kunt er overal naar verwijzen.
  • Naar velden in tabelvariabelen kan worden verwezen met behulp van expressies.

Er is geen automatische conversie, maar tabelvariabelen die zijn gedeclareerd als Common , kunnen gegevens uit elke tabel bevatten.

Bereik van tabelvariabelen

In de meeste opzichten kunnen tabelvariabelen worden beschouwd als objecten, maar in tegenstelling tot objecten worden ze niet expliciet toegewezen. Alleen een variabeledeclaratie is vereist. Alle tabellen zijn compatibel met de algemene tabel, net zoals alle objecten compatibel zijn met de objectklasse . Tabelvariabelen worden gedeclareerd als algemene buffers en kunnen worden gebruikt voor het opslaan van gegevens uit elke tabel. U hebt geen toegang tot tabellen die geen tabelvariabelen hebben. De principes voor het declareren van tabelvariabelen en -objecten zijn hetzelfde, behalve met betrekking tot de toewijzing van ruimte.

Tabelvoorbeelden

De syntaxis maakt het mogelijk om te verwijzen naar velden in records. U kunt bijvoorbeeld TableName gebruiken.( FieldId) syntaxis.

In het volgende voorbeeld wordt de inhoud van de velden in de huidige record in de tabel Klant afgedrukt.

// Declares and allocates space for one CustTable record.
public void myMethod()
{
    CustomerTable custTable;
}

// An example of referencing table variables.
public void printAccountNo()
{
    CustomerTable custTable;
    print custTable.AccountNo;  // Prints the field reference.
}

In het volgende voorbeeld worden de methoden fieldCnt en fieldCnt2Id gebruikt. De methode fieldCnt telt het aantal velden in een tabel, terwijl fieldCnt2Id de id voor een veldnummer retourneert. U kunt bijvoorbeeld de methode fieldCnt2Id gebruiken om te leren dat veldnummer 6 in een tabel de id 54 heeft. Deze conversie is vereist, omdat er geen garantie is dat de id's van de velden in een tabel opeenvolgend zijn.

// An example of the various possibilities for referencing fields in records.
public void printCust()
{
    int i, n, k;
    CustomerTable custTable;
    DictTable dictTable;
    dictTable = new DictTable(custTable.TableId);
    n = dictTable.fieldCnt();
    print "Number of fields in table: ", n;
    for(i=1; i<=n; i++)
    {
        k = dictTable.fieldCnt2Id(i);
        print "The ", dictTable.fieldName(k),
        " field with Id=",k, " contains '",
        custTable.(k), "'";
    }
}