Celle di riferimento (F#)
Le celle di riferimento sono posizioni di archiviazione che consentono di creare valori modificabili con semantica di riferimento.
ref expression
Note
L'operatore ref viene utilizzato prima di un valore per creare una nuova cella di riferimento che incapsuli il valore. È quindi possibile modificare il valore sottostante, in quanto è modificabile.
Una cella di riferimento contiene un valore effettivo e non solo un indirizzo. Quando si crea una cella di riferimento utilizzando l'operatore ref, si crea una copia del valore sottostante come valore modificabile incapsulato.
È possibile dereferenziare una cella di riferimento utilizzando l'operatore ! (punto esclamativo).
Nell'esempio di codice seguente vengono illustrati la dichiarazione e l'utilizzo di celle di riferimento.
// Declare a reference.
let refVar = ref 6
// Change the value referred to by the reference.
refVar := 50
// Dereference by using the ! operator.
printfn "%d" !refVar
L'output è 50.
Le celle di riferimento sono istanze del tipo di record generico Ref, dichiarato come indicato di seguito.
type Ref<'a> =
{ mutable contents: 'a }
Il tipo 'a ref è sinonimo di Ref<'a>. Il primo viene visualizzato dal compilatore e da IntelliSense nell'interfaccia IDE per questo tipo, mentre il secondo rappresenta la definizione sottostante.
L'operatore ref consente di creare una nuova cella di riferimento. Il codice seguente rappresenta la dichiarazione dell'operatore ref.
let ref x = { contents = x }
Nella tabella seguente sono indicate le funzionalità disponibili nella cella di riferimento.
Operatore, membro o campo |
Oggetto di descrizione |
Tipo |
Definizione |
---|---|---|---|
! (operatore di dereferenziazione) |
Restituisce il valore sottostante. |
'a ref -> 'a |
let (!) r = r.contents |
:= (operatore di assegnazione) |
Modifica il valore sottostante. |
'a ref -> 'a -> unit |
let (:=) r x = r.contents <- x |
ref (operatore) |
Incapsula un valore in una nuova cella di riferimento. |
'a -> 'a ref |
let ref x = { contents = x } |
Value (proprietà) |
Ottiene o imposta il valore sottostante. |
unit -> 'a |
member x.Value = x.contents |
contents (campo del record) |
Ottiene o imposta il valore sottostante. |
'a |
let ref x = { contents = x } |
È possibile accedere al valore sottostante in diversi modi. Il valore restituito dall'operatore di dereferenziazione (!) non è un valore assegnabile. Se pertanto si modifica il valore sottostante, è necessario utilizzare l'operatore di assegnazione (:=).
Sia la proprietà Value che il campo contents sono valori assegnabili e possono pertanto essere utilizzati per accedere al valore sottostante o per modificare tale valore, come illustrato nel codice seguente.
let xRef : int ref = ref 10
printfn "%d" (xRef.Value)
printfn "%d" (xRef.contents)
xRef.Value <- 11
printfn "%d" (xRef.Value)
xRef.contents <- 12
printfn "%d" (xRef.contents)
L'output è indicato di seguito.
10
10
11
12
Il campo contents viene fornito per garantire compatibilità con altre versioni di ML e comporta la generazione di un avviso durante la compilazione. Per disabilitare l'avviso, utilizzare l'opzione del compilatore --mlcompatibility. Per ulteriori informazioni, vedere Opzioni del compilatore (F#).
Esempio
Nell'esempio di codice seguente viene illustrato l'utilizzo di celle di riferimento nel passaggio dei parametri. Il tipo Incrementor dispone di un metodo Increment che accetta un parametro che include byref nel tipo di parametro. byref nel tipo di parametro indica che i chiamanti devono passare una cella di riferimento o l'indirizzo di una variabile tipica del tipo specificato, in questo caso int. Nel codice restante viene illustrato come chiamare Increment con entrambi questi tipi di argomenti e come utilizzare l'operatore ref in una variabile per creare una cella di riferimento (ref myDelta1). Viene quindi illustrato l'utilizzo dell'operatore address-of (&) per generare un argomento appropriato. Il metodo Increment viene infine chiamato di nuovo utilizzando una cella di riferimento dichiarata tramite l'associazione let. Nell'ultima riga del codice viene illustrato l'utilizzo dell'operatore ! per dereferenziare la cella di riferimento per la stampa.
type Incrementor(delta) =
member this.Increment(i : int byref) =
i <- i + delta
let incrementor = new Incrementor(1)
let mutable myDelta1 = 10
incrementor.Increment(ref myDelta1)
// Prints 10:
printfn "%d" myDelta1
let mutable myDelta2 = 10
incrementor.Increment(&myDelta2)
// Prints 11:
printfn "%d" myDelta2
let refInt = ref 10
incrementor.Increment(refInt)
// Prints 11:
printfn "%d" !refInt
Per ulteriori informazioni sul passaggio per riferimento, vedere Parametri e argomenti (F#).
Nota
I programmatori C# devono sapere che ref funziona in modo diverso in F# rispetto a C#. L'utilizzo di ref quando si passa un argomento non ha, ad esempio, lo stesso effetto in F# e in C#.
Celle di riferimento evariabili modificabili
Le celle di riferimento e le variabili modificabili possono spesso essere utilizzate in situazioni analoghe. Vi sono tuttavia alcune situazioni in cui non è possibile utilizzare variabili modificabili ed è invece necessario utilizzare una cella di riferimento. In generale, è necessario preferire le variabili modificabili ogni volta che queste sono accettate dal compilatore. Nelle espressioni che generano chiusure il compilatore segnala tuttavia che non è possibile utilizzare variabili modificabili. Le chiusure sono funzioni locali generate da determinate espressioni F#, ad esempio espressioni lambda, espressioni di sequenza, espressioni di calcolo e funzioni sottoposte a currying, in cui vengono utilizzati argomenti applicati parzialmente. Le chiusure generate da queste espressioni vengono archiviate per essere valutate in un secondo momento. Questo processo non è compatibile con le variabili modificabili. Se pertanto è necessario lo stato modificabile in un'espressione di questo tipo, è necessario utilizzare celle di riferimento. Per ulteriori informazioni sulle chiusure, vedere Chiusure (F#).
Nell'esempio di codice seguente viene illustrato lo scenario in cui è necessario utilizzare una cella di riferimento.
// Print all the lines read in from the console.
let PrintLines1() =
let mutable finished = false
while not finished do
match System.Console.ReadLine() with
| null -> finished <- true
| s -> printfn "line is: %s" s
// Attempt to wrap the printing loop into a
// sequence expression to delay the computation.
let PrintLines2() =
seq {
let mutable finished = false
// Compiler error:
while not finished do
match System.Console.ReadLine() with
| null -> finished <- true
| s -> yield s
}
// You must use a reference cell instead.
let PrintLines3() =
seq {
let finished = ref false
while not !finished do
match System.Console.ReadLine() with
| null -> finished := true
| s -> yield s
}
Nel codice precedente la cella di riferimento finished è inclusa nello stato locale, ovvero le variabili che si trovano nella chiusura vengono create e utilizzate completamente all'interno dell'espressione, in questo caso un'espressione di sequenza. Si consideri ciò che si verifica quando le variabili non sono locali. Le chiusure sono in grado di accedere anche allo stato non locale, ma quando questo si verifica le variabili vengono copiate e archiviate dal valore. Questo processo è noto come semantica del valore. I valori vengono archiviati al momento della copia ed eventuali modifiche successive alle variabili non vengono riflesse. Se si desidera tenere traccia delle modifiche di variabili non locali, o, in altre parole, se è necessaria una chiusura che interagisca con lo stato non locale utilizzando la semantica di riferimento, è necessario utilizzare una cella di riferimento.
Nell'esempio di codice seguente viene illustrato l'utilizzo di celle di riferimento nelle chiusure. In questo caso, la chiusura è il risultato dell'applicazione parziale di argomenti della funzione.
// The following code demonstrates the use of reference
// cells to enable partially applied arguments to be changed
// by later code.
let increment1 delta number = number + delta
let mutable myMutableIncrement = 10
// Closures created by partial application and literals.
let incrementBy1 = increment1 1
let incrementBy2 = increment1 2
// Partial application of one argument from a mutable variable.
let incrementMutable = increment1 myMutableIncrement
myMutableIncrement <- 12
// This line prints 110.
printfn "%d" (incrementMutable 100)
let myRefIncrement = ref 10
// Partial application of one argument, dereferenced
// from a reference cell.
let incrementRef = increment1 !myRefIncrement
myRefIncrement := 12
// This line also prints 110.
printfn "%d" (incrementRef 100)
// Reset the value of the reference cell.
myRefIncrement := 10
// New increment function takes a reference cell.
let increment2 delta number = number + !delta
// Partial application of one argument, passing a reference cell
// without dereferencing first.
let incrementRef2 = increment2 myRefIncrement
myRefIncrement := 12
// This line prints 112.
printfn "%d" (incrementRef2 100)
Vedere anche
Riferimenti
Riferimenti per simboli e operatori (F#)