Tipi valore nullable (Riferimenti per C#)
Un tipo valore che ammette i valori Null T?
rappresenta tutti i valori del tipo valore T
sottostante e un valore null aggiuntivo. Ad esempio, è possibile assegnare uno dei tre valori seguenti a una variabile bool?
: true
, false
o 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:
Nullable<T>.HasValue indica se un'istanza di un tipo valore nullable contiene un valore del relativo tipo sottostante.
Nullable<T>.Value ottiene il valore di un tipo sottostante se HasValue è
true
. Se HasValue èfalse
, la proprietà Value genera un'eccezione InvalidOperationException.
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
, mentre 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
, mentre 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 sottostanteT
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#:
- Tipi nullable
- Operatori lifted
- Conversioni nullable implicite
- Conversioni nullable esplicite
- Operatori di conversione lifted