Condividi tramite


Valori Nulli

In questo argomento viene descritto come viene usato il valore Null in F#.

Valori nulli prima di F# 9

Il valore Null non viene in genere usato in F# per valori o variabili. Tuttavia, null viene visualizzato come valore anomalo in determinate situazioni. Se un tipo è definito in F#, null non è consentito come valore regolare, a meno che al tipo non venga applicato l'attributo AllowNullLiteral. Se un tipo è definito in un altro linguaggio .NET, Null è un valore possibile e quando si interagisce con tali tipi, il codice F# potrebbe riscontrare valori Null.

Per un tipo definito in F# e usato rigorosamente da F#, l'unico modo per creare un valore Null usando direttamente la libreria F# consiste nell'usare Unchecked.defaultof o Array.zeroCreate. Tuttavia, per un tipo F# usato da altri linguaggi .NET o se si usa tale tipo con un'API non scritta in F#, ad esempio .NET Framework, possono verificarsi valori Null.

È possibile usare il tipo di option in F# quando è possibile usare una variabile di riferimento con un valore Null possibile in un altro linguaggio .NET. Anziché null, con un tipo F# option, si utilizza il valore dell'opzione None se non c'è alcun oggetto. Usare il valore dell'opzione Some(obj) con un oggetto obj quando è presente un oggetto . Per altre informazioni, vedere Opzioni . Si noti che è comunque possibile inserire un valore di null in un'Opzione se, nel caso di Some x, si verifica che x sia null. Per questo motivo, è importante usare None quando un valore è null.

La parola chiave null è una parola chiave valida in F# ed è necessario usarla quando si usano le API .NET Framework o altre API scritte in un altro linguaggio .NET. Le due situazioni in cui potrebbe essere necessario un valore Null sono quando si chiama un'API .NET e si passa un valore Null come argomento e quando si interpreta il valore restituito o un parametro di output da una chiamata al metodo .NET.

Per passare un valore Null a un metodo .NET, è sufficiente usare la parola chiave null nel codice chiamante. Nell'esempio di codice seguente viene illustrato questo.

open System

// Pass a null value to a .NET method.
let ParseDateTime (str: string) =
    let (success, res) =
        DateTime.TryParse(str, null, System.Globalization.DateTimeStyles.AssumeUniversal)

    if success then Some(res) else None

Per interpretare un valore null ottenuto da un metodo .NET, utilizzare la corrispondenza di pattern se possibile. Nell'esempio di codice seguente viene illustrato come usare il pattern matching per interpretare il valore null restituito da ReadLine quando tenta di leggere oltre la fine di un flusso di input.

// Open a file and create a stream reader.
let fileStream1 =
    try
        System.IO.File.OpenRead("TextFile1.txt")
    with :? System.IO.FileNotFoundException ->
        printfn "Error: TextFile1.txt not found."
        exit (1)

let streamReader = new System.IO.StreamReader(fileStream1)

// ProcessNextLine returns false when there is no more input;
// it returns true when there is more input.
let ProcessNextLine nextLine =
    match nextLine with
    | null -> false
    | inputString ->
        match ParseDateTime inputString with
        | Some(date) -> printfn "%s" (date.ToLocalTime().ToString())
        | None -> printfn "Failed to parse the input."

        true

// A null value returned from .NET method ReadLine when there is
// no more input.
while ProcessNextLine(streamReader.ReadLine()) do
    ()

I valori Null per i tipi F# possono essere generati anche in altri modi, ad esempio quando si usa Array.zeroCreate, che chiama Unchecked.defaultof. È necessario prestare attenzione a questo codice per mantenere incapsulati i valori Null. In una libreria destinata solo a F#, non è necessario verificare la presenza di valori Null in ogni funzione. Se si scrive una libreria per l'interoperabilità con altri linguaggi .NET, potrebbe essere necessario aggiungere controlli per i parametri di input Null e generare un ArgumentNullException, proprio come accade nel codice C# o Visual Basic.

È possibile usare il codice seguente per verificare se un valore arbitrario è Null.

match box value with
| null -> printf "The value is null."
| _ -> printf "The value is not null."

Valori Null introdotti a partire da F# 9

In F# 9 vengono aggiunte funzionalità aggiuntive al linguaggio per gestire i tipi di riferimento che possono avere null come valore. Tali valori sono disattivati per impostazione predefinita: per attivarli, è necessario inserire nel file di progetto la proprietà seguente:

<Nullable>enable</Nullable>

In questo modo viene passato il flag --checknulls+ al compilatore F# e viene impostata una direttiva del preprocessore NULLABLE per la compilazione.

Per aderire esplicitamente alla nullabilità, una dichiarazione di tipo deve essere suffissata con la nuova sintassi:

type | null

Il simbolo della barra | ha il significato di un OR logico nella sintassi, creando un'unione di due set di tipi non contigui: il tipo sottostante e il riferimento nullable. Si tratta dello stesso simbolo sintattico usato per dichiarare più casi di un'unione discriminata F#: type AB = A | B porta il significato di Ao B.

L'annotazione nullable | null può essere usata in tutte le posizioni in cui normalmente viene usato un tipo riferimento:

  • Campi di tipi di unione, tipi di record e tipi personalizzati.
  • Alias di tipo per i tipi esistenti.
  • Applicazioni di un tipo generico.
  • Annotazioni di tipo esplicite per le associazioni let, i parametri o i tipi di ritorno.
  • Annotazioni di tipo per costrutti di programmazione di oggetti, ad esempio membri, proprietà o campi.
type AB = A | B
type AbNull = AB | null

type RecordField = { X: string | null }
type TupleField = string * string | null

type NestedGenerics = { Z : List<List<string | null> | null> | null }

Il simbolo della barra | ha altri utilizzi in F# che potrebbero causare ambiguità sintattiche. In questi casi, le parentesi sono necessarie attorno al tipo annotato con 'null':

// Unexpected symbol '|' (directly before 'null') in member definition
type DUField = N of string | null

Involucrare lo stesso tipo di dati in una coppia di parentesi etichettate ( ) risolve il problema.

type DUField = N of (string | null)

Se usato nella corrispondenza di modelli, | viene usato per separare diverse clausole di corrispondenza dei modelli.

match x with
| ?: string | null -> ...

Questo frammento è effettivamente equivalente al codice che prima effettua un test di tipo sul tipo string, e poi prevede una clausola separata per la gestione dei valori null.

match x with
| ?: string 
| null -> ...

Importante

Le funzionalità aggiuntive correlate a Null sono state aggiunte al linguaggio a scopo di interoperabilità. L'uso di | null nella modellazione dei tipi in F# non è considerato idiomatico per indicare le informazioni mancanti. A tale scopo, utilizzare le opzioni (come descritto in precedenza). Altre informazioni sulle convenzioni di correlate ai valori Null nella guida di stile.

Vedere anche