Tipus de dades compostes X++

Nota

Els grups d'interès comunitari ara s'han traslladat de Yammer a Microsoft Viva Engage. Per unir-te a una comunitat Viva Engage i participar en les últimes discussions, omple el formulari Sol·licita accés a la Comunitat Viva Engage de Finances i Operacions i tria la comunitat a la qual vols unir-te.

En aquest article es descriuen els tipus de dades compostos en X++. Els tipus de dades compostos en X++ són matrius, contenidors, classes com a tipus de dades, delegats com a tipus de dades i taules com a tipus de dades.

Matriu

Una matriu és una variable que conté una llista d'elements que tenen el mateix tipus de dades. S'accedeix als elements d'una matriu mitjançant índexs enters. Utilitzeu una instrucció separada per inicialitzar cada element d'una matriu. Quan utilitzeu un tipus de dades de contenidor o un objecte de matriu per crear una col·lecció, podeu inicialitzar diversos elements mitjançant una sola instrucció. Per defecte, tots els elements d'una matriu tenen el valor per defecte del tipus de dades de la matriu. Hi ha tres tipus de matrius: matrius dinàmiques, matrius de longitud fixa i parcialment matrius de discs.

  • Matrius dinàmiques : aquestes matrius es declaren mitjançant una opció de matriu buida. En altres paraules, només tenen claudàtors ([]).
  • Matrius de longitud fixa : aquestes matrius poden contenir el nombre d'elements especificat a la declaració. Les matrius de longitud fixa es declaren com matrius dinàmiques, però s'inclou una opció de longitud entre claudàtors.
  • Parcialment en matrius de discs : aquestes matrius es declaren com a matrius dinàmiques o matrius de longitud fixa que tenen una opció addicional que declara quants elements s'han de mantenir a la memòria. Els altres elements s'emmagatzemen al disc i es carreguen automàticament quan s'hi fa referència.

X++ només admet matrius unidimensionals. Tanmateix, podeu imitar el comportament de diversos índexs de matriu. (Per a més informació, vegeu la secció Índexs de matriu múltiples ). Les variables d'objectes i taules es poden declarar com a matrius. Per exemple, aquesta funcionalitat s'utilitza a les línies d'adreces de l'aplicació estàndard. Una classe de col·lecció de matrius us permet emmagatzemar objectes en una matriu.

Els índexs de matriu comencen a 1. Es fa referència al primer element de la matriu com a [1], al segon element es fa referència a [2], i així successivament. La sintaxi següent s'utilitza per accedir a un element de matriu: ArrayItemReference = ArrayVariable [ Índex ]. En aquesta sintaxi, ArrayVariable és l'identificador de la matriu i Index és el número de l'element de la matriu. L'índex pot ser una expressió entera. L'element zero [0] s'utilitza per esborrar la matriu. Si s'assigna un valor a l'índex 0 en una matriu, tots els elements de la matriu es restableixen al seu valor per defecte.

L'assignació d'una matriu sencera a una altra es realitza per referència.

Exemples de matrius

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];
}

Índexs de matriu múltiples

Alguns llenguatges, com ara C++ i C#, us permeten declarar matrius que tenen més d'un índex. En altres paraules, podeu definir "matrius de matrius". En X++, no podeu crear directament diversos índexs de matriu perquè només s'admeten matrius unidimensionals. Tanmateix, podeu implementar diversos índexs mitjançant el mètode que es descriu en aquesta secció. Per exemple, voleu declarar una matriu que té dues dimensions, per contenir una quantitat que es guanya per país o regió per dimensió. Hi ha 10 països/regions i tres dimensions. En C++ i C#, declareu la matriu següent.

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

Tanmateix, X++ no admet aquesta declaració. En lloc d'això, podeu definir una matriu unidimensional on el nombre d'elements és el producte dels elements de cada dimensió. Aquest és un exemple.

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)
}

Contenidor

Un objecte contenidor és una llista dinàmica d'elements que conté tipus de dades primitius o tipus de dades compostos. Un contenidor és útil quan heu de passar diversos tipus de valors entre els nivells de client i servidor. Tanmateix, si teniu previst afegir repetidament a una llista en un bucle, un contenidor no és una bona opció. Els contenidors són més adequats per a processos que no impliquen una modificació excessiva de la mida o el contingut del contenidor. Quan un contenidor experimenta addicions excessives de dades, el rendiment general del sistema pot disminuir perquè les dades del contenidor s'han de copiar repetidament i s'ha d'assignar espai nou repetidament.

Un contenidor no és una classe. Un contenidor conté una seqüència ordenada de valors primitius o altres contenidors. A causa de la flexibilitat de qualsevol tipus, un contenidor ofereix una bona manera d'emmagatzemar valors de diferents tipus junts. Es pot emmagatzemar un contenidor a la base de dades. Un contenidor és un dels tipus de columna que podeu seleccionar quan utilitzeu l'Explorador d'aplicacions per afegir una columna a una taula. Un contenidor s'assembla lleugerament a una matriu o col·leccions, com ara les classes List o Stack . Tanmateix, mai no podeu canviar la mida o el contingut d'un contenidor després de crear-lo.

Les sentències X++ que semblen modificar un contenidor estan construint internament un nou contenidor i copiant valors segons sigui necessari. Fins i tot l'assignació d'un contenidor a una altra variable de contenidor crea una nova còpia del contenidor. Totes aquestes operacions poden afectar el rendiment. A les funcions que proporcionen accés a un contenidor (com ara conPeek), el contenidor està basat en 1, no en 0. La indexació es basa en 1 per a matrius. El valor per defecte d'un contenidor és buit. El contenidor no conté cap valor. Algunes declaracions que utilitzen contenidors poden aparèixer per modificar un contenidor, però, dins del sistema, els contenidors no es modifiquen mai. En canvi, les dades del contenidor original es combinen amb les dades de l'ordre per crear un contenidor nou. Podeu crear un contenidor nou mitjançant una de les funcions següents: conDel, conIns o conPoke.

A més, la classe Global té mètodes estàtics per manejar contenidors. Aquests mètodes inclouen con2ArraySource, con2Buf, con2List, con2Str, containerFromXmlNode, conView i str2Con. Hi ha diverses funcions intrínseques per a la manipulació d'un contenidor, com ara conIns i conPeek. La funció X++ conPeek retorna un tipus anytype , per tant, és més fàcil llegir els valors d'un contenidor quan no coneixeu el tipus de cada valor. Es pot assignar un anytype a qualsevol tipus de valor X++, sempre que el valor es pugui convertir. El codi és més fàcil de llegir quan evita les conversions explícites de tipus de dades. Per tant, assigneu valors d'un contenidor al mateix tipus de dades que s'ha utilitzat per posar el valor al contenidor. No heu d'assignar un contenidor a un anytype perquè és possible que el sistema no pugui determinar les conversions correctes. En aquests casos, es poden produir comportaments o errors inesperats.

Comparació del contenidor amb altres opcions

El tipus de contenidor s'assembla a altres construccions, com ara matrius i classes de col·lecció, com ara List i Stack. La diferència entre un contenidor i List és que una instància de la classe List és mutable. Un objecte List primer assigna més espai del que consumeixen les seves dades. Després, a mesura que s'afegeixen dades, s'omple l'espai. Aquest comportament és més eficient que assignar més espai cada vegada que s'afegeix un element. Una actualització d'una llista es realitza més ràpidament que operacions similars en un contenidor.

Quan construïu un objecte List , determineu el tipus de dades que pot emmagatzemar l'objecte List . Aquesta restricció és menys flexible per a una llista que per a un contenidor. Tanmateix, podeu emmagatzemar objectes en una llista, mentre que un contenidor només pot emmagatzemar tipus de valor. La diferència entre un contenidor i una matriu és que una matriu només pot contenir elements del seu tipus declarat. Podeu assignar espai de memòria per a una matriu i omplir aquest espai amb valors més endavant. Per exemple, pots emplenar valors en un bucle. Aquest comportament és eficient i funciona bé. Quan vulgueu crear un contenidor nou afegint dades noves, podeu utilitzar l'operador o la += funció conIns . L'operador += és l'alternativa més ràpida. Utilitzeu la funció conIns només quan vulgueu afegir dades noves abans de l'últim índex de les dades originals.

No podeu emmagatzemar referències d'objectes en contenidors. Quan el compilador detecta un intent d'emmagatzemar una referència d'objecte en un contenidor, emet un missatge d'error. Si el tipus de l'element que s'afegeix al contenidor és anytype, el compilador no pot determinar si el valor és un tipus de referència. En aquest cas, el compilador permet l'intent. Tot i que el compilador no diagnostica el codi com a erroni, es llançarà un error en temps d'execució.

Exemples de contenidors

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];
}

Classes com a tipus de dades

Una classe és una definició de tipus que descriu tant variables com mètodes per a instàncies de la classe. (Les instàncies d'una classe també es coneixen com a objectes.) Una classe és només una definició d'objectes, i tots els objectes són nuls quan es declaren. A l'Explorador d'aplicacions, cada classe d'aplicació del node Classes és un tipus de dades. Podeu declarar variables d'aquests tipus al codi. Podeu construir instàncies d'una classe i assignar les instàncies a variables.

Les classes es poden imbricar en el codi font. Les classes imbricades només estan disponibles dins de formularis (com ara una classe que amplia FormRun) i s'utilitzen per representar controls, fonts de dades o camps de dades. Una decoració d'atribut, com ara la decoració d'atribut d'una classe o d'un mètode, pot ometre el sufix del nom de l'atribut si el sufix és Attribute. Per tant, X++ permet [MyFavorite] en lloc de requerir [MyFavoriteAttribute]. A més, ara s'apliquen atributs als controladors de delegats i mètodes, per assignar els controladors a aquests objectius.

A l'AX 2012 i versions anteriors, es podia designar un mètode per executar-se al client o al servidor. No obstant això, en aplicacions financeres i d'operacions, tot el codi X++ compilat s'executa com a .NET Common Intermediate Language (CIL) al servidor. Ja no hi ha cap codi que s'avaluï al lloc del client o al navegador. Per tant, ara s'ignoren les paraules clau client i servidor . Tot i que aquestes paraules clau no causen un error de compilació si s'utilitzen, no s'han d'utilitzar en cap codi nou.

Variables membres privades i protegides

Anteriorment, totes les variables membres que es definien en una classe estaven protegides. Ara podeu fer explícita la visibilitat de les variables membres afegint les paraules clau private, protected i public . La interpretació d'aquests modificadors és òbvia i està alineada amb la semàntica dels mètodes:

  • private : la variable membre només es pot utilitzar dins de la classe on està definida.
  • protected – La variable membre es pot utilitzar a la classe on està definida i a totes les subclasses d'aquesta classe.
  • public – La variable membre es pot utilitzar a qualsevol lloc. És visible fora dels límits de la jerarquia de classes on està definit.

Per defecte, les variables membres que no estan adornades amb un modificador explícit encara estan protegides. Tanmateix, com a pràctica recomanada, heu d'especificar explícitament la visibilitat. Com hem descrit anteriorment, quan una variable membre es defineix com a pública, es pot accedir a ella fora de la classe on està definida. En aquest cas, heu d'especificar un qualificador que designi l'objecte que allotja la variable. Per especificar el qualificador, utilitzeu la notació de punts, com ho feu per a les crides al mètode.

A l'exemple següent, s'accedeix al camp1 mitjançant el qualificador explícit this . En aquest cas, potser no seria una bona idea fer pública una variable membre, perquè aquest enfocament exposa el funcionament intern de la classe als seus consumidors i, per tant, crea una forta dependència entre la implementació de la classe i els seus consumidors. Sempre hauríeu d'intentar dependre només d'un contracte, no d'una implementació.

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

Constructors estàtics i camps estàtics

Els camps estàtics són camps que es declaren mitjançant la paraula clau static . Conceptualment, els camps estàtics s'apliquen a la classe, no a les instàncies de la classe. Es garanteix que els constructors estàtics s'executin abans que es facin crides estàtiques o crides d'instància a la classe. L'execució del constructor estàtic és relativa a la sessió de l'usuari. Mai no crideu el constructor estàtic explícitament. En lloc d'això, el compilador generarà codi per assegurar-se que el constructor es crida exactament una vegada, abans que es cridi a qualsevol altre mètode de la classe. Un constructor estàtic s'utilitza per inicialitzar qualsevol dada estàtica o realitzar una acció que només s'ha de realitzar una vegada. No podeu proporcionar paràmetres per al constructor estàtic i s'ha de marcar amb la paraula clau static .

// 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();
}

Elements de classe a l'Explorador d'aplicacions

Sota la majoria de nodes de classe de l'Explorador d'aplicacions, hi ha dos nodes especials: un node classDeclaration i un node nou . Una classDeclaration sempre conté la paraula clau de classe X++. Es poden incloure paraules clau addicionals, com ara extensions, per modificar la classe. Aquest node també pot contenir declaracions de variables membres.

A l'exemple següent, les variables m_priority i m_rectangle són membres de la classe.

// 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);
    }
}

Un operador nou conté lògica que s'executa quan s'utilitza l'operador nou per crear una instància de la classe. La lògica del nou mètode pot construir un objecte i assignar-lo a una variable declarada a classDeclaration. Cada classe només pot tenir un mètode nou . Tanmateix, en el nou mètode, sovint hauríeu de trucar al nou mètode de la classe base. Per cridar el nou mètode de la classe base, crida a super().

L'exemple següent mostra el nou mètode per a la classe YourDerivedClass a l'exemple anterior de classDeclaration . En aquest nou mètode, el codi construeix una instància de la classe Rectangle . La instància s'assigna a la variable m_rectangle . La paraula clau this que s'utilitza a l'exemple és opcional, però, si l'incloeu, l'IntelliSense pot ser més útil.

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

Recollida d'escombraries

Finalment, durant el temps d'execució, la majoria dels objectes ja no tenen cap variable que els apunti. El sistema escaneja aquests objectes i els esborra de la memòria. L'espai de memòria queda disponible per a altres usos. La classe Object té un mètode que s'anomena finalize. Tanmateix, el mètode finalitzar no és un destructor. El temps d'execució mai crida al mètode finalize, fins i tot quan un objecte es recull com a escombraries.

Classes de sistema

A l'Explorador d'aplicacions, aClasses de > del sistema, hi ha una llista de les classes del nucli o classes del sistema. Les classes del sistema no s'escriuen en X++ i no es pot veure el seu codi font. No podeu afegir classes del sistema. Les classes del sistema solen tenir un mètode nou , però no tenen un node classDeclaration . Cada classe d'aplicació amplia implícitament la classe del sistema Object. Algunes classes del sistema s'estenen amb una classe d'aplicació que té un nom similar. Per exemple, xClassFactory està ampliat per ClassFactory. En aquests casos, no heu d'utilitzar la classe system. Per obtenir més informació, vegeu "Substituir classes d'aplicació per classes de sistema" a Classes i mètodes.

Mètodes d'extensió

La característica del mètode d'extensió us permet afegir mètodes d'extensió a una classe de destinació escrivint els mètodes en una classe d'extensió independent. S'apliquen les regles següents:

  • La classe d'extensió ha de ser estàtica.
  • El nom de la classe d'extensió ha d'acabar amb el sufix de deu caràcters _Extension, però, no hi ha cap restricció a la part del nom que precedeix el sufix.
  • Tots els mètodes d'extensió de la classe d'extensió s'han de declarar com a estàtics públics.
  • El primer paràmetre de cada mètode d'extensió és el tipus que s'estén el mètode d'extensió. Tanmateix, quan es crida al mètode d'extensió, la persona que truca no ha de passar res per al primer paràmetre. En canvi, el sistema passa automàticament l'objecte requerit per al primer paràmetre.
  • La destinació d'un mètode d'extensió ha de ser un tipus d'objecte d'aplicació de classe, taula, visualització o mapa.

Una classe d'extensió pot contenir mètodes estàtics privats o protegits. Aquests mètodes s'utilitzen normalment per als detalls de la implementació i no s'exposen com a extensions. La tècnica del mètode d'extensió no afecta el codi font de la classe que s'estén, per tant, l'addició a la classe no requereix sobrecapes.

Les actualitzacions de la classe de destinació mai no es veuen afectades per cap mètode d'extensió existent. Si una actualització de la classe de destinació afegeix un mètode que té el mateix nom que el mètode d'extensió, ja no es pot accedir al mètode d'extensió mitjançant objectes de la classe de destinació. La tècnica del mètode d'extensió utilitza la mateixa sintaxi delimitada per punts que s'utilitza sovint per cridar mètodes d'instància normals. Els mètodes d'extensió poden accedir a tots els artefactes públics de la classe de destinació, però no poden accedir a res que estigui protegit o privat. Per tant, els mètodes d'extensió es poden considerar un tipus de sucre sintàctic. Independentment del tipus de destinació, s'utilitza una classe d'extensió per afegir mètodes d'extensió al tipus. Per exemple, una taula d'extensió no s'utilitza per afegir mètodes a una taula i no hi ha una taula d'extensió.

// 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;
    }
}

Delegats com a tipus de dades

Un delegat recull mètodes que s'hi subscriuen. El delegat especifica la signatura de paràmetre que han de compartir tots els seus mètodes de subscriptor. Quan es crida al delegat, el delegat truca a cadascun dels seus subscriptors. Un delegat mai retorna un valor i no pot tenir un valor per defecte. Al principi, tots els delegats no tenen mètodes subscrits. No hi ha límit en el nombre de paràmetres que pot declarar un delegat i no hi ha cap limitació en el tipus d'aquests paràmetres. El cos delegat sempre està buit, perquè l'únic propòsit del delegat és definir el contracte que han de complir els subscriptors. Un delegat no ha d'estar definit en una classe. Els delegats també es poden definir en una taula, formulari o consulta.

Exemples de delegats

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.
    }
    
    
}

Taules com a tipus de dades

Totes les taules es poden tractar com a definicions de classe. Una variable de taula es pot considerar una instància (objecte) de la definició de taula (classe). Per a cada camp d'una variable de taula, el valor per defecte és buit. Podeu adreçar camps i crear mètodes a les taules. Els mètodes es poden invocar en instàncies de la taula. Per manipular (és a dir, llegir, actualitzar, inserir i suprimir) registres a les taules, heu de declarar com a mínim una variable de taula que pugui mantenir el registre enfocat. Com a pràctica recomanada, heu d'utilitzar el nom de la taula com a nom de la variable, però utilitzeu una lletra minúscula inicial. Aquí hi ha algunes diferències importants entre taules i objectes:

  • No podeu assignar espai per a les variables de taula. L'assignació es fa implícitament.
  • Els camps de les variables de taula són públics. Podeu fer-hi referència a qualsevol lloc.
  • Es pot fer referència als camps de les variables de taula mitjançant expressions.

No hi ha cap conversió automàtica, però les variables de taula declarades com a comunes poden contenir dades de qualsevol taula.

Abast de les variables de taula

En la majoria dels aspectes, les variables de taula es poden considerar objectes, però, a diferència dels objectes, no s'assignen explícitament. Només es requereix una declaració de variables. Totes les taules són compatibles amb la taula Common, de la mateixa manera que tots els objectes són compatibles amb la classe Object. Les variables de taula es declaren com a memòries intermèdies comunes i es poden utilitzar per contenir dades de qualsevol taula. No podeu accedir a les taules que no tenen variables de taula. Els principis per declarar variables i objectes de taula són els mateixos, excepte pel que fa a l'assignació d'espai.

Exemples de taules

La sintaxi permet diverses possibilitats per fer referència als camps dels registres. Per exemple, podeu utilitzar el TableName.( FieldId).

L'exemple següent imprimeix el contingut dels camps del registre actual de la taula Client.

// 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.
}

L'exemple següent utilitza els mètodes fieldCnt i fieldCnt2Id . El mètode fieldCnt compta el nombre de camps d'una taula, mentre que fieldCnt2Id retorna l'ID d'un número de camp. Per exemple, podeu utilitzar el mètode fieldCnt2Id per saber que el camp número 6 d'una taula té l'identificador 54. Aquesta conversió és necessària, ja que no hi ha cap garantia que els identificadors dels camps d'una taula siguin consecutius.

// 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), "'";
    }
}