Tipi valore nullable (Riferimenti per C#)

Un tipo valore nullableT? (che ammette i valori Null) rappresenta tutti i valori del tipo di valoreT sottostante e un valore Null aggiuntivo. Ad esempio, è possibile assegnare uno dei tre valori seguenti a una variabile bool?: true, falseo null. Un tipo di valore T sottostante non può essere esso stesso un tipo di valore nullable.

Qualsiasi tipo di valore nullable è un'istanza della struttura generica System.Nullable<T>. È possibile fare riferimento a un tipo valore nullable con un tipo T sottostante in uno dei formati intercambiabili seguenti: Nullable<T> o T?.

In genere si usa un tipo valore nullable quando è necessario rappresentare il valore non definito di un tipo di valore sottostante. Ad esempio, una variabile booleana, o bool, può essere solo true o false. Tuttavia, in alcune applicazioni un valore di variabile può essere indefinito o mancante. Ad esempio, un campo di database può contenere true o false, oppure potrebbe non contenere alcun valore, ovvero NULL. È possibile usare il tipo bool? in tale scenario.

Dichiarazione e assegnazione

Poiché un tipo valore è convertibile in modo implicito nel tipo valore nullable corrispondente, è possibile assegnare un valore a una variabile di un tipo valore nullable come si farebbe per il tipo di valore sottostante. È anche possibile assegnare il valore null. Ad esempio:

double? pi = 3.14;
char? letter = 'a';

int m2 = 10;
int? m = m2;

bool? flag = null;

// An array of a nullable value type:
int?[] arr = new int?[10];

Il valore predefinito di un tipo valore nullable rappresenta null, ovvero è un'istanza la cui proprietà Nullable<T>.HasValue restituisce false.

Esame di un'istanza di un tipo valore nullable

È possibile usare l'operatore is con un criterio di tipo per esaminare un'istanza di un tipo valore nullable per null e recuperare un valore di un tipo sottostante:

int? a = 42;
if (a is int valueOfA)
{
    Console.WriteLine($"a is {valueOfA}");
}
else
{
    Console.WriteLine("a does not have a value");
}
// Output:
// a is 42

È sempre possibile usare le proprietà di sola lettura seguenti per esaminare e ottenere un valore di una variabile di tipo valore nullable:

Il codice nell'esempio seguente usa la proprietà HasValue per verificare se la variabile contiene un valore prima di visualizzarla:

int? b = 10;
if (b.HasValue)
{
    Console.WriteLine($"b is {b.Value}");
}
else
{
    Console.WriteLine("b does not have a value");
}
// Output:
// b is 10

È anche possibile confrontare una variabile di un tipo valore nullable con null anziché usare la proprietà HasValue, come illustrato nell'esempio seguente:

int? c = 7;
if (c != null)
{
    Console.WriteLine($"c is {c.Value}");
}
else
{
    Console.WriteLine("c does not have a value");
}
// Output:
// c is 7

Conversione da un tipo di valore nullable a un tipo sottostante

Se si vuole assegnare un valore di tipo valore nullable a una variabile di tipo valore non nullable, potrebbe essere necessario specificare il valore da assegnare al posto di null. Usare l'operatore di coalescenza di valori Null?? per eseguire questa operazione (è anche possibile usare il metodo Nullable<T>.GetValueOrDefault(T) per lo stesso scopo):

int? a = 28;
int b = a ?? -1;
Console.WriteLine($"b is {b}");  // output: b is 28

int? c = null;
int d = c ?? -1;
Console.WriteLine($"d is {d}");  // output: d is -1

Se si vuole usare il valore predefinito del tipo di valore sottostante al posto di null, usare il metodo Nullable<T>.GetValueOrDefault().

È anche possibile eseguire il cast esplicito di un tipo valore nullable a un tipo non nullable, come illustrato nell'esempio seguente:

int? n = null;

//int m1 = n;    // Doesn't compile
int n2 = (int)n; // Compiles, but throws an exception if n is null

Se in fase di esecuzione il valore di un tipo nullable è null, il cast esplicito genera un'eccezione InvalidOperationException.

Un tipo valore non nullable T viene convertito in modo implicito nel tipo di valore nullable corrispondente T?.

Operatori lifted

Gli operatori unari e binari predefiniti o gli operatori di overload supportati da un tipo valore T sono supportati anche dal tipo di valore nullable T? corrispondente. Questi operatori, noti anche come operatori lifted, producono null se uno o entrambi gli operandi sono null; in caso contrario, l'operatore utilizza i valori contenuti dei relativi operandi per calcolare il risultato. Ad esempio:

int? a = 10;
int? b = null;
int? c = 10;

a++;        // a is 11
a = a * c;  // a is 110
a = a + b;  // a is null

Nota

Per il tipo bool?, gli operatori predefiniti & e | non si attengono alle regole descritte in questa sezione: il risultato di una valutazione degli operatori può essere non Null, anche se uno degli operandi è null. Per altre informazioni, vedere la sezione Operatori logici booleani nullable dell'articolo Operatori logici booleani.

Per gli operatori di confronto<, >, <= e >=, se uno o entrambi gli operandi sono null, il risultato è false; in caso contrario, vengono confrontati i valori contenuti degli operandi. Non presupporre che poiché un particolare confronto (ad esempio, <=) restituisce false, il confronto opposto (>) restituisce true. L'esempio seguente mostra che 10

  • non è né maggiore o uguale a null
  • né minore di null
int? a = 10;
Console.WriteLine($"{a} >= null is {a >= null}");
Console.WriteLine($"{a} < null is {a < null}");
Console.WriteLine($"{a} == null is {a == null}");
// Output:
// 10 >= null is False
// 10 < null is False
// 10 == null is False

int? b = null;
int? c = null;
Console.WriteLine($"null >= null is {b >= c}");
Console.WriteLine($"null == null is {b == c}");
// Output:
// null >= null is False
// null == null is True

Per l'operatore di uguaglianza==, se entrambi gli operandi sono null, il risultato è true, se solo uno degli operandi è null, il risultato è false; in caso contrario, vengono confrontati i valori contenuti degli operandi.

Per l'operatore di disuguaglianza!=, se entrambi gli operandi sono null, il risultato è false, se solo uno degli operandi è null, il risultato è true; in caso contrario, vengono confrontati i valori contenuti degli operandi.

Se esiste una conversione definita dall'utente tra due tipi valore, è anche possibile usare la stessa conversione tra i tipi di valore nullable corrispondenti.

Boxing e unboxing

Un'istanza di un tipo valore nullable T? viene sottoposta a boxing come indicato di seguito:

  • Se HasValue restituisce false, viene prodotto il riferimento Null.
  • Se HasValue restituisce true, viene sottoposto a boxing un valore del tipo valore sottostante T e non l'istanza di Nullable<T>.

È possibile eseguire la conversione unboxing del tipo valore T sottoposto a boxing nel tipo valore nullable T? corrispondente, come illustrato nell'esempio seguente:

int a = 41;
object aBoxed = a;
int? aNullable = (int?)aBoxed;
Console.WriteLine($"Value of aNullable: {aNullable}");

object aNullableBoxed = aNullable;
if (aNullableBoxed is int valueOfA)
{
    Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
}
// Output:
// Value of aNullable: 41
// aNullableBoxed is boxed int: 41

Procedura: Identificare un tipo valore nullable

Nell'esempio seguente viene illustrato come determinare se un'istanza System.Type rappresenta un tipo valore nullable costruito, ovvero il tipo System.Nullable<T> con un parametro di tipo specificato T:

Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

// Output:
// int? is nullable value type
// int is non-nullable value type

Come illustrato nell'esempio, si usa l'operatore typeof per creare un'istanza System.Type.

Se si vuole determinare se un'istanza è di un tipo nullable, non usare il metodo Object.GetType per ottenere un'istanza Type da testare con il codice precedente. Quando si chiama il metodo Object.GetType in un'istanza di un tipo nullable, viene eseguita la conversione boxing dell'istanza a Object. Poiché la conversione boxing di un'istanza non Null di un tipo valore nullable è equivalente alla conversione boxing di un valore del tipo sottostante, GetType restituisce un’istanza Type che rappresenta il tipo sottostante di un tipo nullable:

int? a = 17;
Type typeOfA = a.GetType();
Console.WriteLine(typeOfA.FullName);
// Output:
// System.Int32

Inoltre, non usare l’operatore is per determinare se un'istanza è di un tipo valore nullable. Come illustrato nell'esempio seguente, non è possibile distinguere i tipi di istanze di un tipo nullable e del relativo tipo sottostante con l'operatore is:

int? a = 14;
if (a is int)
{
    Console.WriteLine("int? instance is compatible with int");
}

int b = 17;
if (b is int?)
{
    Console.WriteLine("int instance is compatible with int?");
}
// Output:
// int? instance is compatible with int
// int instance is compatible with int?

Usare invece Nullable.GetUnderlyingType dal primo esempio e l'operatore typeof per verificare se un'istanza è di un tipo valore nullable.

Nota

I metodi descritti in questa sezione non sono applicabili nel caso di tipi riferimento nullable.

Specifiche del linguaggio C#

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

Vedi anche