Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Observação
Os grupos de interesse da comunidade passaram do Yammer para o Microsoft Viva Engage. Para se juntar a uma comunidade Viva Engage e participar nas discussões mais recentes, preencha o formulário Solicitar acesso à Comunidade Viva Engage de Finanças e Operações e escolha a comunidade à qual pretende juntar-se.
Este artigo descreve tipos de dados compostos no X++. Os tipos de dados compostos em X++ são matrizes, contêineres, classes como tipos de dados, delegados como tipos de dados e tabelas como tipos de dados.
Array
Uma matriz é uma variável que contém uma lista de itens que têm o mesmo tipo de dados. Os elementos de uma matriz são acessados usando índices inteiros. Você usa uma instrução separada para inicializar cada elemento em uma matriz. Ao usar um tipo de dados de contêiner ou um objeto de matriz para criar uma coleção, você pode inicializar vários elementos usando uma única instrução. Por padrão, todos os itens em uma matriz têm o valor padrão do tipo de dados na matriz. Existem três tipos de matrizes: matrizes dinâmicas, matrizes de comprimento fixo e, parcialmente, matrizes em disco.
- Matrizes dinâmicas – Essas matrizes são declaradas usando uma opção de matriz vazia. Por outras palavras, têm apenas parênteses ([]).
- Matrizes de comprimento fixo – Essas matrizes podem conter o número de itens especificado na declaração. Matrizes de comprimento fixo são declaradas como matrizes dinâmicas, mas uma opção de comprimento é incluída entre colchetes.
- Parcialmente em matrizes de disco – Essas matrizes são declaradas como matrizes dinâmicas ou matrizes de comprimento fixo que têm uma opção extra que declara quantos itens devem ser mantidos na memória. Os outros itens são armazenados no disco e são carregados automaticamente quando são referenciados.
X++ suporta apenas matrizes unidimensionais. No entanto, você pode imitar o comportamento de vários índices de matriz. (Para obter mais informações, consulte a seção Vários índices de matrizes ). As variáveis em objetos e tabelas podem ser declaradas como matrizes. Por exemplo, essa funcionalidade é usada em linhas de endereço no aplicativo padrão. Uma classe de coleção de matriz permite armazenar objetos em uma matriz.
Os índices de matriz começam em 1. O primeiro item na matriz é referenciado como [1], o segundo item é referenciado como [2] e assim por diante. A sintaxe a seguir é usada para acessar um elemento de matriz: ArrayItemReference = ArrayVariable [ Index ]. Nessa sintaxe, ArrayVariable é o identificador da matriz e Index é o número do elemento da matriz. Índice pode ser uma expressão inteira. O item zero [0] é usado para limpar a matriz. Se um valor for atribuído ao índice 0 em uma matriz, todos os elementos da matriz serão redefinidos para seu valor padrão.
Uma atribuição de uma matriz inteira para outra é executada por referência.
Exemplos de matrizes
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];
}
Vários índices de matriz
Algumas linguagens, como C++ e C#, permitem declarar matrizes que têm mais de um índice. Em outras palavras, você pode definir "matrizes de matrizes". No X++, não é possível criar diretamente vários índices de matrizes porque apenas matrizes unidimensionais são suportadas. No entanto, você pode implementar vários índices usando o método descrito nesta seção. Por exemplo, você deseja declarar uma matriz que tem duas dimensões, para manter uma quantia que é obtida por país/região por dimensão. Existem 10 países/regiões e três dimensões. Em C++ e C#, você declara a seguinte matriz.
// This is C# or C++ code, not X++ code.
long earning[10, 3];
No entanto, o X++ não suporta esta declaração. Em vez disso, você pode definir uma matriz unidimensional onde o número de elementos é o produto dos elementos em cada dimensão. Vejamos um exemplo.
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)
}
Contentor
Um objeto de contêiner é uma lista dinâmica de itens que contém tipos de dados primitivos ou tipos de dados compostos. Um contêiner é útil quando você deve passar vários tipos de valores entre as camadas de cliente e servidor. No entanto, se você planeja adicionar repetidamente a uma lista em um loop, um contêiner não é uma boa escolha. Os recipientes são mais adequados para processos que não envolvem modificação excessiva no tamanho ou conteúdo do recipiente. Quando um contêiner sofre adições excessivas de dados, o desempenho geral do sistema pode diminuir porque os dados do contêiner devem ser copiados repetidamente e o novo espaço deve ser alocado repetidamente.
Um contêiner não é uma classe. Um contêiner contém uma sequência ordenada de valores primitivos ou outros contêineres. Devido à flexibilidade de qualquer tipo, um contêiner oferece uma boa maneira de armazenar valores de diferentes tipos juntos. Um contêiner pode ser armazenado no banco de dados. Um contêiner é um dos tipos de coluna que você pode selecionar ao usar o Application Explorer para adicionar uma coluna a uma tabela. Um contêiner se assemelha ligeiramente a uma matriz ou coleções, como as classes List ou Stack . No entanto, você nunca pode alterar o tamanho ou o conteúdo de um contêiner depois que ele é criado.
As instruções X++ que parecem modificar um contêiner estão criando internamente um novo contêiner e copiando valores conforme necessário. Até mesmo uma atribuição de um contêiner a outra variável de contêiner cria uma nova cópia do contêiner. Todas essas operações podem afetar o desempenho. Nas funções que fornecem acesso a um contêiner (como conPeek), o contêiner é baseado em 1, não em 0. A indexação é baseada em 1 para matrizes. O valor padrão de um contêiner está vazio. O contêiner não contém valores. Algumas instruções que usam contêineres podem parecer modificar um contêiner, no entanto, dentro do sistema, os contêineres nunca são modificados. Em vez disso, os dados do contêiner original são combinados com os dados do comando para criar um novo contêiner. Você pode criar um novo contêiner usando uma das seguintes funções: conDel, conIns ou conPoke.
Além disso, a classe Global tem métodos estáticos para manipular contêineres. Esses métodos incluem con2ArraySource, con2Buf, con2List, con2Str, containerFromXmlNode, conView e str2Con. Existem várias funções intrínsecas para o manuseamento de um contentor, tais como conIns e conPeek. A função conPeek X++ retorna um tipo anytype , portanto, é mais fácil ler os valores de um contêiner quando você não sabe o tipo de cada valor. Um anytype pode ser atribuído a qualquer tipo de valor X++, desde que o valor possa ser convertido. Seu código é mais fácil de ler quando evita conversões explícitas de tipo de dados. Portanto, atribua valores de um contêiner ao mesmo tipo de dados que foi usado para colocar o valor no contêiner. Você não deve atribuir um contêiner a um qualquer tipo porque o sistema pode não ser capaz de determinar as conversões corretas. Nesses casos, podem ocorrer erros ou comportamentos inesperados.
Comparando contêiner com outras opções
O tipo de contêiner se assemelha a outras construções, como matrizes e classes de coleção, como List e Stack. A diferença entre um contêiner e List é que uma instância da classe List é mutável. Um objeto List primeiro aloca mais espaço do que seus dados consomem. Em seguida, à medida que os dados são adicionados, o espaço é preenchido. Esse comportamento é mais eficiente do que alocar mais espaço toda vez que um elemento é adicionado. Uma atualização de uma Lista executa operações mais rápidas do que semelhantes em um contêiner.
Ao construir um objeto List , você determina o tipo de dados que o objeto List pode armazenar. Essa restrição é menos flexível para uma Lista do que para um contêiner. No entanto, você pode armazenar objetos em uma lista, enquanto um contêiner pode armazenar apenas tipos de valor. A diferença entre um contêiner e uma matriz é que uma matriz pode conter apenas itens de seu tipo declarado. Você pode alocar espaço de memória para uma matriz e preencher esse espaço com valores mais tarde. Por exemplo, você pode preencher valores em um loop. Este comportamento é eficiente e tem um bom desempenho. Quando quiser criar um novo contêiner anexando novos dados, você pode usar o += operador ou a função conIns . O += operador é a alternativa mais rápida. Use a função conIns somente quando quiser adicionar novos dados antes do último índice dos dados originais.
Não é possível armazenar referências de objetos em contêineres. Quando o compilador deteta uma tentativa de armazenar uma referência de objeto em um contêiner, ele emite uma mensagem de erro. Se o tipo do elemento que é adicionado ao contêiner for anytype, o compilador não poderá determinar se o valor é um tipo de referência. Neste caso, o compilador permite a tentativa. Embora o compilador não diagnostique o código como errado, um erro será lançado em tempo de execução.
Exemplos de contêineres
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 como tipos de dados
Uma classe é uma definição de tipo que descreve variáveis e métodos para instâncias da classe. (As instâncias de uma classe também são conhecidas como objetos.) Uma classe é apenas uma definição para objetos, e todos os objetos são nulos quando são declarados. No Application Explorer, cada classe de aplicativo no nó Classes é um tipo de dados. Você pode declarar variáveis desses tipos em seu código. Você pode construir instâncias de uma classe e atribuir as instâncias a variáveis.
As classes podem ser aninhadas no código-fonte. As classes aninhadas estão disponíveis apenas dentro de formulários (como uma classe que estende FormRun) e são usadas para representar controles, fontes de dados ou campos de dados. Uma decoração de atributo, como a decoração de atributos em uma classe ou um método, pode omitir o sufixo do nome do atributo se o sufixo for Atributo. Portanto, o X++ permite [MyFavorite] em vez de exigir [MyFavoriteAttribute]. Além disso, os atributos agora são aplicados aos manipuladores de delegados e métodos, para mapear os manipuladores para esses destinos.
No AX 2012 e em versões anteriores, você pode designar um método para ser executado no cliente ou no servidor. No entanto, em aplicativos de finanças e operações, todo o código X++ compilado é executado como .NET Common Intermediate Language (CIL) no servidor. Não há mais nenhum código que seja avaliado no site do cliente ou no navegador. Portanto, as palavras-chave do cliente e do servidor agora são ignoradas. Embora essas palavras-chave não causem um erro de compilação se forem usadas, elas não devem ser usadas em nenhum código novo.
Variáveis de membros privadas e protegidas
Anteriormente, todas as variáveis de membro que eram definidas em uma classe eram protegidas. Agora você pode tornar explícita a visibilidade das variáveis de membro adicionando as palavras-chave private, protected e public . A interpretação destes modificadores é óbvia e está alinhada com a semântica dos métodos:
- private – A variável membro só pode ser usada dentro da classe onde está definida.
- protected – A variável member pode ser usada na classe onde está definida e em todas as subclasses dessa classe.
- public – A variável membro pode ser usada em qualquer lugar. É visível fora dos limites da hierarquia de classe onde é definido.
Por padrão, as variáveis de membro que não são adornadas com um modificador explícito ainda são protegidas. No entanto, como prática recomendada, você deve especificar explicitamente a visibilidade. Como descrevemos anteriormente, quando uma variável membro é definida como pública, ela pode ser acessada fora da classe onde está definida. Nesse caso, você deve especificar um qualificador que designe o objeto que está hospedando a variável. Para especificar o qualificador, use a notação de ponto, como faz para chamadas de método.
No exemplo a seguir, o campo 1 é acessado usando o qualificador explícito . Neste caso, pode não ser uma boa ideia tornar público um membro variável, porque essa abordagem expõe o funcionamento interno da classe aos seus consumidores e, portanto, cria uma forte dependência entre a implementação da classe e seus consumidores. Deve sempre tentar depender apenas de um contrato, não de uma implementação.
public class AnotherClass3
{
int field1;
str field2;
void new()
{
this.field1 = 1; // Explicit object designated.
field2 = "Banana"; // 'this' assumed, as usual.
}
}
Construtores estáticos e campos estáticos
Campos estáticos são campos que são declarados usando a palavra-chave estática . Conceitualmente, os campos estáticos se aplicam à classe, não às instâncias da classe. Os construtores estáticos são garantidos para serem executados antes que quaisquer chamadas estáticas ou chamadas de instância sejam feitas para a classe. A execução do construtor estático é relativa à sessão do usuário. Você nunca chama o construtor estático explicitamente. Em vez disso, o compilador irá gerar código para certificar-se de que o construtor é chamado exatamente uma vez, antes de qualquer outro método na classe é chamado. Um construtor estático é usado para inicializar quaisquer dados estáticos ou executar uma ação que deve ser executada apenas uma vez. Você não pode fornecer parâmetros para o construtor estático e ele deve ser marcado com a palavra-chave estática .
// 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();
}
Elementos de classe no Application Explorer
Na maioria dos nós de classe no Application Explorer, há dois nós especiais: um nó classDeclaration e um novo nó. Um classDeclaration sempre contém a palavra-chave da classe X++. Palavras-chave adicionais, como extends, podem ser incluídas para modificar a classe. Este nó também pode conter declarações de variáveis membro.
No exemplo a seguir, as variáveis m_priority e m_rectangle são membros da 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);
}
}
Um novo operador contém lógica que é executada quando o novo operador é usado para criar uma instância da classe. A lógica no novo método pode construir um objeto e atribuir esse objeto a uma variável declarada no classDeclaration. Cada classe pode ter apenas um novo método. No entanto, no novo método, muitas vezes você deve chamar o novo método da classe base. Para chamar o novo método da classe base, chame super().
O exemplo a seguir mostra o novo método para a classe YourDerivedClass no exemplo classDeclaration anterior. Nesse novo método, o código constrói uma instância da classe Rectangle . A instância é atribuída à variável m_rectangle . A palavra-chave usada no exemplo é opcional, no entanto, se você incluí-la, o IntelliSense pode ser mais ú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);
}
Recolha de lixo
Eventualmente, durante o tempo de execução, a maioria dos objetos não tem mais nenhuma variável que aponte para eles. O sistema verifica esses objetos e os apaga da memória. O espaço de memória torna-se então disponível para outros usos. A classe Object tem um método chamado finalize. No entanto, o método finalize não é um destruidor. O tempo de execução nunca chama o método finalize , mesmo quando um objeto é coletado como lixo.
Classes de sistema
No Application Explorer, em Classes de Documentação> doSistema, há uma lista das classes do kernel ou classes do sistema. As classes de sistema não são escritas em X++ e você não pode ver seu código-fonte. Não é possível adicionar classes do sistema. As classes de sistema geralmente têm um novo método, mas não têm um nó classDeclaration . Cada classe de aplicativo estende implicitamente a classe de sistema Object . Algumas classes de sistema são estendidas por uma classe de aplicativo que tem um nome semelhante. Por exemplo, xClassFactory é estendido por ClassFactory. Nesses casos, você não deve usar a classe do sistema. Para obter mais informações, consulte "Substituir classes de aplicativo por classes de sistema" em Classes e métodos.
Métodos de extensão
O recurso de método de extensão permite adicionar métodos de extensão a uma classe de destino escrevendo os métodos em uma classe de extensão separada. Aplicam-se as seguintes regras:
- A classe de extensão deve ser estática.
- O nome da classe de extensão deve terminar com o sufixo de dez caracteres _Extension, no entanto, não há nenhuma restrição na parte do nome que precede o sufixo.
- Cada método de extensão na classe de extensão deve ser declarado como estático público.
- O primeiro parâmetro em cada método de extensão é o tipo que o método de extensão estende. No entanto, quando o método de extensão é chamado, o chamador não deve passar nada para o primeiro parâmetro. Em vez disso, o sistema passa automaticamente o objeto necessário para o primeiro parâmetro.
- O destino de um método de extensão deve ser um tipo de objeto de aplicativo de classe, tabela, exibição ou mapa.
Uma classe de extensão pode conter métodos estáticos privados ou protegidos. Esses métodos são normalmente usados para detalhes de implementação e não são expostos como extensões. A técnica do método de extensão não afeta o código-fonte da classe que ele estende, portanto, a adição à classe não requer camadas excessivas.
As atualizações para a classe de destino nunca são afetadas por quaisquer métodos de extensão existentes. Se uma atualização para a classe de destino adiciona um método que tem o mesmo nome que seu método de extensão, seu método de extensão não pode mais ser alcançado por meio de objetos da classe de destino. A técnica de método de extensão usa a mesma sintaxe delimitada por pontos que você costuma usar para chamar métodos de instância regular. Os métodos de extensão podem acessar todos os artefatos públicos da classe de destino, mas não podem acessar nada que seja protegido ou privado. Portanto, os métodos de extensão podem ser considerados um tipo de açúcar sintático. Independentemente do tipo de destino, uma classe de extensão é usada para adicionar métodos de extensão ao tipo. Por exemplo, uma tabela de extensão não é usada para adicionar métodos a uma tabela e não existe uma tabela de extensão.
// 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;
}
}
Delegados como tipos de dados
Um delegado coleta métodos que o assinam. O delegado especifica a assinatura de parâmetro que todos os seus métodos de assinante devem compartilhar. Quando o delegado é chamado, o delegado chama cada um de seus assinantes. Um delegado nunca retorna um valor e não pode ter um valor padrão. No início, cada delegado não tem métodos inscritos. Não há limite para o número de parâmetros que um delegado pode declarar, e não há limitação no tipo desses parâmetros. O órgão delegado está sempre vazio, porque o único objetivo do delegado é definir o contrato que os assinantes devem cumprir. Um delegado não precisa ser definido em uma classe. Os delegados também podem ser definidos em uma tabela, formulário ou consulta.
Delegar exemplos
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.
}
}
Tabelas como tipos de dados
Todas as tabelas podem ser tratadas como definições de classe. Uma variável de tabela pode ser considerada uma instância (objeto) da definição de tabela (classe). Para cada campo em uma variável de tabela, o valor padrão é vazio. Você pode endereçar campos e criar métodos em tabelas. Os métodos podem ser invocados em instâncias da tabela. Para manipular (ou seja, ler, atualizar, inserir e excluir) registros em tabelas, você deve declarar pelo menos uma variável de tabela que possa manter o registro em foco. Como prática recomendada, você deve usar o nome da tabela como o nome da variável, mas usar uma letra minúscula inicial. Aqui estão algumas diferenças importantes entre tabelas e objetos:
- Não é possível alocar espaço para variáveis de tabela. A alocação é feita implicitamente.
- Os campos nas variáveis da tabela são públicos. Você pode consultá-los em qualquer lugar.
- Os campos em variáveis de tabela podem ser referenciados usando expressões.
Não há conversão automática, mas as variáveis de tabela declaradas como Common podem conter dados de qualquer tabela.
Âmbito das variáveis do quadro
Na maioria dos aspetos, as variáveis de tabela podem ser consideradas objetos, no entanto, ao contrário dos objetos, elas não são explicitamente alocadas. Apenas é necessária uma declaração variável. Todas as tabelas são compatíveis com a tabela Common , assim como todos os objetos são compatíveis com a classe Object . As variáveis de tabela são declaradas como buffers comuns e podem ser usadas para armazenar dados de qualquer tabela. Não é possível acessar tabelas que não tenham variáveis de tabela. Os princípios para declarar variáveis de tabela e objetos são os mesmos, exceto no que diz respeito à alocação de espaço.
Exemplos de tabelas
A sintaxe permite várias possibilidades de referenciação de campos em registros. Por exemplo, você pode usar o TableName.( FieldId).
O exemplo a seguir imprime o conteúdo dos campos no registro atual na tabela Customer.
// 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.
}
O exemplo a seguir usa os métodos fieldCnt e fieldCnt2Id . O método fieldCnt conta o número de campos em uma tabela, enquanto fieldCnt2Id retorna a ID de um número de campo. Por exemplo, você pode usar o método fieldCnt2Id para saber que o campo número 6 em uma tabela tem a ID 54. Esta conversão é necessária, porque não há garantia de que os IDs dos campos em uma tabela são consecutivos.
// 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), "'";
}
}