Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Nota
I gruppi di interesse della comunità si sono ora spostati da Yammer a Microsoft Viva Engage. Per unirti a una community Viva Engage e partecipare alle ultime discussioni, compila il modulo Richiedi accesso alla Comunità Viva Engage Finanza e Operazioni e scegli la community a cui vuoi entrare.
Questo articolo descrive le variabili in X++.
- Una variabile è un identificatore che punta a una posizione di memoria in cui sono memorizzate le informazioni di un tipo di dati specifico. Le dimensioni, la precisione, il valore predefinito, le funzioni di conversione implicite ed esplicite e l'intervallo dipendono dal tipo di dati della variabile.
- L'ambito di una variabile definisce l'area del codice in cui è possibile accedere a un elemento.
- Le variabili di istanza sono dichiarate nelle dichiarazioni di classe, e puoi accedervi da qualsiasi metodo della classe o da metodi che estendono la classe.
- Le variabili locali possono essere accessibili solo nel blocco in cui le hai definite.
- Quando dichiari una variabile, alloci memoria e inizializzi la variabile al valore predefinito.
- È possibile assegnare valori sia ai campi statici che ai campi di istanza come parte dell'istruzione di dichiarazione.
- Puoi dichiarare variabili ovunque in un blocco di codice di un metodo. Non è necessario dichiararli all'inizio di un processo.
- Le costanti sono variabili in cui il valore non può essere cambiato quando dichiari la variabile. Usano la parola chiave const o readonly .
- Le costanti differiscono dai campi di sola lettura in un solo modo. Puoi assegnare un valore ai campi di sola lettura una sola volta, e quel valore non cambia mai. Puoi assegnare al campo il suo valore sia in linea, nel punto in cui il campo è dichiarato, sia nel costruttore.
Quando si dichiarano variabili di tipi gestiti che non sono stati creati in X++, sono disponibili due opzioni. È possibile qualificare completamente i nomi dei tipi nella dichiarazione includendo lo spazio dei nomi completo oppure aggiungere un'istruzione using al file e quindi omettere lo spazio dei nomi dal nome del tipo.
Esempi di variabili
// An example of two valid variable names.
str variableName;
CustInfo custNumber;
// An example of simultaneously declaring and initializing a variable.
real pi = 3.14159265359; // Assigns value of pi to 12 significant digits.
// An example of initializing an object by using the new method on the class.
Access accessObject = new Access(); // Simple call to the new method in the Access class.
// An example of multiple declarations using integers.
int i,j; // Declares 2 integers, i and j.
// An example of multiple declarations using an array.
int a[100,5], b=1; // Declares array with 100 integers with 5 in memory and b as an integer with value 1.
// An example of how variable scopes work.
class ScopeExample
{
// The variable a is declared within the class.
int a;
// Because the method below is declared within the class,
// it can access all the variables defined within the class.
void aNewMethod()
{
// The variable b is declared within the method.
// It can only be accessed by this method.
int b;
}
}
// An example of an assignment of field members inline.
public class FieldAssignmentExample
{
int field1 = 1;
str field2 = "Banana";
void new()
{
// ...
}
}
class ConstantExample
{
// An example of a constant being declared at the class level.
public const str MyContent = 'SomeValue';
public int ResultSoFar()
{
return 1;
}
}
// The constants can then be referenced by using the double-colon syntax.
str value = ConstantExample::MyContent;
// If you're in the scope of the class where the const is defined,
// you can omit the type name prefix (ConstantExample in this example).
// An example of the using clause where the alias can denote
// namespaces and classes.
using System;
using IONS=System.IO; // Namespace alias.
using Alist=System.Collections.ArrayList; // Class alias.
public class NamespaceExample
{
public static void Main(Args a)
{
Int32 I; // Alternative to System.Int32.
Alist al; // Using a class alias.
al = new Alist();
str s;
al.Add(1);
IONS.Path::ChangeExtension(@"c:\tmp\test.xml", ".txt");
}
}
Var
Puoi dichiarare una variabile senza fornire esplicitamente il tipo della variabile se il compilatore può determinare il tipo dall'espressione di inizializzazione. La variabile è ancora fortemente tipata a un tipo inequivocabile.
Puoi usare var solo sulle dichiarazioni in cui fornisci espressioni di inizializzazione. Il compilatore deduce il tipo dall'espressione di inizializzazione. In alcuni casi, questo approccio può rendere il codice più facile da leggere.
Usa var per dichiarare variabili locali in queste situazioni:
- Quando il tipo della variabile è evidente dal lato destro dell'assegnazione
- Quando il tipo esatto non è importante
- Per le dichiarazioni dei contatori del ciclo for
- Per gli oggetti eliminabili all'interno delle istruzioni using
Non usare var quando il tipo non è chiaro dall'espressione di inizializzazione.
Esempi di var
// When the type of a variable is clear from
// the context, use var in the declaration.
var var1 = "This is clearly a string.";
var var2 = 27; // This is an integer (not a real).
var i = System.Convert::ToInt32(3.4);
// Don't use var when the type of the variable is not clear
// from the context. Use an explicit type instead.
int var4 = myObject.ResultSoFar();
Dichiara ovunque
Ora puoi fornire dichiarazioni ovunque tu possa fornire dichiarazioni. Una dichiarazione è sintatticamente un'affermazione, quindi è una dichiarazione.
Puoi fornire dichiarazioni immediatamente prima di usare la variabile, e non devi dichiarare tutte le variabili in un unico posto. Pertanto, hai un controllo preciso sull'ambito delle tue variabili.
Puoi dare alle variabili scope più piccoli, al di fuori dei quali non puoi fare riferimento alle variabili. La durata della variabile è l'ambito in cui viene dichiarata. Puoi iniziare gli scope a livello di blocco (all'interno delle istruzioni composte), in for statement e nell'uso delle statement.
Ci sono diversi vantaggi nel rendere piccoli i cannocchiali di piccole dimensioni:
- La leggibilità è migliorata.
- Si riduce il rischio che una variabile venga riutilizzata in modo inappropriato durante la manutenzione a lungo termine del codice.
- È più facile effettuare il refactoring del codice. Puoi copiare il codice senza preoccuparti che le variabili possano essere riutilizzate in contesti dove non dovrebbero essere riutilizzate.
Nel seguente esempio, dichiari il loop counter all'interno dell'istruzione for in cui lo usi.
void MyMethod()
{
for (int i = 0; i < 10; i++)
{
info(strfmt("i is %1", i));
}
}
L'ambito della variabile è l'istruzione for stessa, e include le parti dell'espressione condizione e dell'aggiornamento del ciclo. Non puoi usare il valore al di fuori di questo ambito.
Nel seguente esempio, quando il compilatore raggiunge l'istruzione info , restituisce il seguente messaggio di errore: "'i' non è dichiarato."
void MyMethod()
{
for (int i = 0; i < 10; i++)
{
if (i == 7)
{
break;
}
}
// The next statement causes a compiler error.
info(strfmt("Found: %1", i));
}
È inoltre possibile definire l'ambito delle variabili in un'istruzione using , come illustrato nell'esempio seguente.
static void AnotherMethod()
{
str textFromFile;
using (System.IO.StreamReader sr = new System.IO.StreamReader("c:\\test.txt"))
{
textFromFile = sr.ReadToEnd();
}
}
Quando usi un oggetto che implementa IDisposable, dichiara e istanzia quell'oggetto in un'istruzione using . L'istruzione using chiama correttamente il metodo Dispose sull'oggetto, anche se si verifica un'eccezione mentre si chiamano i metodi sull'oggetto. È possibile ottenere lo stesso risultato inserendo l'oggetto all'interno di un blocco try e quindi chiamando in modo esplicito Dispose in un blocco finally . In effetti, il compilatore traduce l'istruzione using proprio in questo modo.
L'esempio seguente mostra alcune delle caratteristiche descritte in questa sezione.
// loop variable declared within the loop: It will
// not be misused outside the loop
for(int i = 1; i < 10; i++)
{
// Because this value is not used from outside the loop,
// its declaration belongs in this smaller scope.
str s = int2str(i);
info(s);
}
Per evitare confusione, il compilatore restituisce un messaggio di errore se si prova a introdurre una variabile che nasconde un'altra variabile in un ambito di inclusione, o anche nello stesso ambito. Ad esempio, il seguente codice fa restituire al compilatore il seguente messaggio diagnostico: "Una variabile locale chiamata 'i' non può essere dichiarata in questo ambito perché darebbe un significato diverso a 'i', che è già usato in un ambito genitore o attuale per indicare qualcos'altro."
{
int i;
{
int i;
}
}
Costanti, variabili di sola lettura e macro
Il linguaggio supporta pienamente il concetto di macro. Le costanti offrono i seguenti vantaggi rispetto ai macro:
- È possibile aggiungere un commento alla documentazione a una costante, ma non al valore di una macro. Alla fine, il servizio linguistico prende questo commento e fornisce informazioni utili all'utente.
- IntelliSense riconosce una costante.
- Viene fatto riferimento a una costante. Pertanto, è possibile trovare tutti i riferimenti per una costante specifica, ma non per una macro.
- Una costante è soggetta ai modificatori di accesso. È possibile utilizzare i modificatori private, protected e public . L'accessibilità delle macro non è definita in modo rigoroso.
- Le variabili costanti hanno ambito, mentre le macro non hanno ambito.
- È possibile visualizzare il valore di una costante o di una variabile di sola lettura nel debugger.
Le macro che definisci negli ambiti di classe (cioè nelle dichiarazioni di classe) sono effettivamente disponibili in tutti i metodi di tutte le classi derivate. Questa caratteristica era originariamente un bug nell'implementazione della macro del compilatore legacy. Tuttavia, molti programmatori di applicazioni ora ne approfittano. Il compilatore X++ rispetta ancora questa caratteristica, ma non scrivere nuovo codice che la utilizzi. Questa funzione ha anche un effetto significativo sulle prestazioni del compilatore.
Come opzione, puoi usare una macro a livello di dichiarazione di classe, ma con un riferimento specifico al valore in quella macro. Questo approccio non ha alcun impatto sulle prestazioni del compilatore. Puoi abilitare le macro standard esistenti per avere riferimenti incrociati (così come tutte le capacità di una variabile costante) nel tuo sviluppo.
private const int RetryNum = #OCCRetryCount#RetryNum;
Puoi dichiarare costanti a livello di classe, come mostrato nell'esempio seguente.
private const str MyConstant = 'SomeValue';
Puoi fare riferimento alle costanti usando la sintassi dei due punti (::).
str value = MyClass::MyConstant;
Se rientri nell'ambito della classe in cui definisci la costante (const), puoi omettere il prefisso nome del tipo (MyClass nell'esempio precedente). Pertanto, è possibile implementare facilmente il concetto di libreria macro. L'elenco dei simboli macro diventa una classe con definizioni const pubbliche.
È inoltre possibile definire le costanti solo come variabili. Il compilatore mantiene l'invariante in modo che il valore non possa essere modificato.
{
const int Blue = 0x0000FF;
const int Green = 0x00FF00;
const int Red = 0xFF0000;
}
Puoi assegnare un valore ai campi di sola lettura solo una volta, e quel valore non cambia mai. Puoi assegnare al campo il suo valore sia in linea, nel punto in cui il campo è dichiarato, sia nel costruttore.
Valori Null per i tipi di dati
Molti altri sistemi di gestione dei database (DBMS) supportano il concetto di valori nulli , ma X++ non supporta questo concetto. Una variabile in X++ ha sempre un tipo e un valore. Tuttavia, per ogni tipo di dato, un valore è considerato nullo (ad esempio, quando viene eseguito il metodo validateField table).
| Tipo | Valore considerato come null |
|---|---|
| Data | 1900-01-01 |
| Enumerazione | Un elemento il cui valore è impostato su 0 |
| Numero intero | 0 |
| real | 0.0 |
| Corda | Una stringa vuota |
| Ora | 00:00:00 |
| Utcdatetime | Qualsiasi valore la cui parte relativa alla data è impostata su 1900-01-01, indipendentemente dal valore della parte relativa all'ora. Ad esempio, il valore 1900-01-01T22:33:44 viene considerato null. Si noti che l'istruzione print di X++ mostra qualsiasi valore utcDateTime che ha la sua parte di data impostata a 1900-01-01 come vuoto. Solo il metodo Global::info mostra il valore 1900-01-01T00:00:00 come vuoto. Tale valore è il valore del metodo DateTimeUtil::MinValue . |
Quando il metodo validateField verifica se un utente inserisce un valore in un campo obbligatorio, se 0 non viene accettato in un campo di tipo intero , se la prima voce non viene accettata in un campo di tipo enum , e così via. Inoltre, nelle istruzioni SQL X++, i valori elencati nella tabella precedente restituiscono false in un confronto booleano. Tuttavia, nelle istruzioni X++ non SQL, gli operatori di uguale e relazionali funzionano con questi valori, proprio come funzionano con altri valori. Le variabili del tipo contenitore e le classi e le variabili del tipo tabella possono essere null nel senso DBMS tradizionale. Un tipo di tabella è null se tutti i relativi campi hanno il loro valore null .
Colata
Il cast si riferisce alle assegnazioni tra variabili i cui tipi dichiarati si trovano entrambi nella stessa catena di ereditarietà. Un cast è un down-cast o un up-cast. Si consideri la seguente gerarchia di classi.
La classe MotorVehicle non è correlata alla classe Animal, anche se entrambe derivano da Object. Un up-cast avviene quando assegni un'espressione di un tipo derivato a un tipo base:
Animal a = new Horse();
Un downcast si verifica quando assegni un'espressione di tipo base a una variabile derivata.
Horse h = new Animal();
X++ supporta sia gli up-cast che i down-cast. Tuttavia, i down-casting sono pericolosi e dovresti evitarli ogni volta che è possibile. L'esempio riportato nel codice precedente fallisce con un InvalidCastException runtime at, poiché l'assegnazione non ha senso. X++ supporta l'associazione tardiva su alcuni tipi, ad esempio .objectformrun Questo supporto significa che il compilatore non diagnostica errori in tempo di compilazione quando vede un metodo chiamato su quei tipi, se quel metodo non è dichiarato esplicitamente sul tipo. Si presume che lo sviluppatore sappia cosa sta facendo. Ad esempio, il codice seguente può essere trovato in un modulo.
Object o = element.args().caller();
o.MyMethod(3.14, “Banana”);
Il compilatore non è in grado di controllare i parametri o i valori restituiti per il MyMethod metodo, poiché questo metodo non è dichiarato nella classe di oggetti. Durante l'esecuzione, la chiamata viene effettuata tramite riflessione, che è molto più lenta rispetto alle chiamate che non richiedono riflessione. Le chiamate a metodi definiti sui tipi di late binding vengono controllate. Ad esempio, la chiamata a ToString nell'esempio seguente:
o.ToString(45);
causa un errore di compilazione:
'Object.toString' expects 0 argument(s), but 1 specified.
perché il ToString metodo è definito nella object classe. C'è una differenza rispetto all'implementazione nelle versioni precedenti di X++ (AX2012 e precedenti) legata al fatto che i metodi potevano essere chiamati su oggetti non correlati, purché il nome del metodo fosse corretto, anche se i profili dei parametri non erano completamente corretti. Questo supporto non è più disponibile.
Esempio - casting
public class MyClass2
{
public static void Main(Args a)
{
Object obj = new Car();
Horse horse = obj; // exception now thrown
horse.run(); // Used to call car.run()!
}
}
Usa liberamente gli is operatori e as nel tuo codice. Usa l'operatore is per verificare se l'espressione fornita è di un tipo particolare (inclusi i tipi derivati). L'operatore as esegue il casting nel tipo dato e restituisce null se un cast non è possibile.