Istruzioni di dichiarazione

Un'istruzione di dichiarazione dichiara una nuova variabile locale, una costante locale o variabile di riferimento locale. Per dichiarare una variabile locale, specificarne il tipo e il nome. È possibile dichiarare più variabili dello stesso tipo in un'istruzione, come illustrato nell'esempio seguente:

string greeting;
int a, b, c;
List<double> xs;

In un'istruzione di dichiarazione è anche possibile inizializzare una variabile con il relativo valore iniziale:

string greeting = "Hello";
int a = 3, b = 2, c = a + b;
List<double> xs = new();

Gli esempi precedenti specificano in modo esplicito il tipo di una variabile. È anche possibile consentire al compilatore di dedurre il tipo di una variabile dalla relativa espressione di inizializzazione. A tale scopo, usare la parola chiave var anziché il nome di un tipo. Per altre informazioni, vedere Variabili locali tipizzate in modo implicito.

Per dichiarare una costante locale, usare la parola chiave const, come illustrato nell'esempio seguente:

const string Greeting = "Hello";
const double MinLimit = -10.0, MaxLimit = -MinLimit;

Quando si dichiara una costante locale, è anche necessario inizializzarla.

Per informazioni sulle variabili di riferimento locali, vedere la sezione Variabili di riferimento.

Variabili locali tipizzate in modo implicito

Quando si dichiara una variabile locale, è possibile consentire al compilatore di dedurre il tipo della variabile dall'espressione di inizializzazione. A tale scopo, usare la parola chiave var anziché il nome di un tipo:

var greeting = "Hello";
Console.WriteLine(greeting.GetType());  // output: System.String

var a = 32;
Console.WriteLine(a.GetType());  // output: System.Int32

var xs = new List<double>();
Console.WriteLine(xs.GetType());  // output: System.Collections.Generic.List`1[System.Double]

Come illustrato nell'esempio precedente, le variabili locali tipizzate in modo implicito sono fortemente tipizzate.

Nota

Quando si usa var nel contesto con riconoscimento nullable abilitato e il tipo di un'espressione di inizializzazione è un tipo riferimento, il compilatore deduce sempre un tipo riferimento nullable anche se il tipo di un'espressione di inizializzazione non è nullable.

La parola chiave var viene generalmente usata con un'espressione di chiamata di un costruttore. L'uso di var consente di non ripetere un nome di tipo in una dichiarazione di variabile e nella creazione di un'istanza dell'oggetto, come illustrato nell'esempio seguente:

var xs = new List<int>();

È possibile usare un'espressione new con tipo di destinazione come alternativa:

List<int> xs = new();
List<int>? ys = new();

Quando si usano tipi anonimi, è necessario usare variabili locali tipizzate in modo implicito. L'esempio seguente mostra un'espressione di query che usa un tipo anonimo per contenere il nome e il numero di telefono di un cliente:

var fromPhoenix = from cust in customers
                  where cust.City == "Phoenix"
                  select new { cust.Name, cust.Phone };

foreach (var customer in fromPhoenix)
{
    Console.WriteLine($"Name={customer.Name}, Phone={customer.Phone}");
}

Nell'esempio precedente non è possibile specificare in modo esplicito il tipo della variabile fromPhoenix. Il tipo è IEnumerable<T> ma in questo caso T è un tipo anonimo e non è possibile specificarne il nome. Ecco perché è necessario usare var. Per lo stesso motivo, è necessario usare var quando si dichiara la variabile di iterazione customer nell'istruzione foreach.

Per altre informazioni sulle variabili locali tipizzate in modo implicito, vedere Variabili locali tipizzate in modo implicito.

Nella corrispondenza dei criteri, la parola chiave var viene usata in un criterio var.

Variabili di riferimento

Quando si dichiara una variabile locale e si aggiunge la parola chiave ref prima del tipo della variabile, si dichiara una variabile di riferimento o una variabile ref locale:

ref int aliasOfvariable = ref variable;

Una variabile di riferimento è una variabile che fa riferimento a un'altra variabile, denominata referente. Ovvero, una variabile di riferimento è un alias per il relativo referente. Quando si assegna un valore a una variabile di riferimento, tale valore viene assegnato al referente. Quando si legge il valore di una variabile di riferimento, viene restituito il valore del referente. L'esempio seguente illustra questo comportamento:

int a = 1;
ref int aliasOfa = ref a;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (1, 1)

a = 2;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (2, 2)

aliasOfa = 3;
Console.WriteLine($"(a, aliasOfa) is ({a}, {aliasOfa})");  // output: (a, aliasOfa) is (3, 3)

Usare l'operatore di assegnazione ref = ref per modificare il referente di una variabile di riferimento, come illustrato nell'esempio seguente:

void Display(int[] s) => Console.WriteLine(string.Join(" ", s));

int[] xs = [0, 0, 0];
Display(xs);

ref int element = ref xs[0];
element = 1;
Display(xs);

element = ref xs[^1];
element = 3;
Display(xs);
// Output:
// 0 0 0
// 1 0 0
// 1 0 3

Nell'esempio precedente la variabile di riferimento element viene inizializzata come alias per il primo elemento della matrice. ref viene quindi riassegnata per fare riferimento all'ultimo elemento della matrice.

È possibile definire una variabile locale ref readonly. Non è possibile assegnare un valore a una variabile ref readonly. Tuttavia, è possibile riassegnare ref tale variabile di riferimento, come illustrato nell'esempio seguente:

int[] xs = [1, 2, 3];

ref readonly int element = ref xs[0];
// element = 100;  error CS0131: The left-hand side of an assignment must be a variable, property or indexer
Console.WriteLine(element);  // output: 1

element = ref xs[^1];
Console.WriteLine(element);  // output: 3

È possibile assegnare un valore restituito di riferimento a una variabile di riferimento, come illustrato nell'esempio seguente:

using System;

public class NumberStore
{
    private readonly int[] numbers = [1, 30, 7, 1557, 381, 63, 1027, 2550, 511, 1023];

    public ref int GetReferenceToMax()
    {
        ref int max = ref numbers[0];
        for (int i = 1; i < numbers.Length; i++)
        {
            if (numbers[i] > max)
            {
                max = ref numbers[i];
            }
        }
        return ref max;
    }

    public override string ToString() => string.Join(" ", numbers);
}

public static class ReferenceReturnExample
{
    public static void Run()
    {
        var store = new NumberStore();
        Console.WriteLine($"Original sequence: {store.ToString()}");
        
        ref int max = ref store.GetReferenceToMax();
        max = 0;
        Console.WriteLine($"Updated sequence:  {store.ToString()}");
        // Output:
        // Original sequence: 1 30 7 1557 381 63 1027 2550 511 1023
        // Updated sequence:  1 30 7 1557 381 63 1027 0 511 1023
    }
}

Nell'esempio precedente il metodo GetReferenceToMax è un metodo returns-by-ref. Non restituisce il valore massimo stesso, ma un valore restituito di riferimento che è un alias dell'elemento della matrice che contiene il valore massimo. Il metodo Run assegna un valore restituito di riferimento alla variabile di riferimento max. Assegnando quindi a max, aggiorna lo spazio di archiviazione interno dell'istanza store. È anche possibile definire un metodo ref readonly. I chiamanti di un metodo ref readonly non possono assegnare un valore al valore restituito di riferimento.

La variabile di iterazione dell'istruzione foreach può essere una variabile di riferimento. Per altre informazioni, vedere la sezione relativa all'istruzione foreach dell'articolo Istruzioni di iterazione.

Negli scenari critici per le prestazioni, l'uso delle variabili di riferimento e dei valori restituiti potrebbe migliorare le prestazioni evitando operazioni di copia potenzialmente costose.

Il compilatore garantisce che una variabile di riferimento non sopravviva il suo referente e rimanga valida per l'intera durata. Per altre informazioni, vedere la sezione Contesti sicuri di riferimento della specifica del linguaggio C#.

Per informazioni sui campi ref, vedere la sezione campi ref dell'articolo tipi di strutture ref.

scoped ref

La parola chiave contestuale scoped limita la durata di un valore. Il modificatore scoped limita rispettivamente la durata ref-safe-to-escape or safe-to-escape al metodo corrente. In effetti, l'aggiunta del modificatore scoped asserisce che il codice non estenderà la durata della variabile.

È possibile applicare scoped a un parametro o a una variabile locale. Il modificatore scoped può essere applicato ai parametri e alle variabili locali quando il tipo è ref struct. In caso contrario, il modificatore scoped può essere applicato solo alle variabili di riferimento locali. Ciò include le variabili locali dichiarate con il modificatore ref e i parametri dichiarati con i modificatori in, ref o out.

Il modificatore scoped viene aggiunto in modo implicito a this nei metodi dichiarati in struct, nei parametri out e nei parametri ref quando il tipo è ref struct.

Specifiche del linguaggio C#

Per altre informazioni, vedere le sezioni seguenti delle specifiche del linguaggio C#:

Per altre informazioni sul modificatore scoped, vedere la nota sulla proposta miglioramenti dello struct di basso livello.

Vedi anche