Partager via


Cellules de référence (F#)

Les cellules de référence sont des emplacements de stockage qui vous permettent de créer des valeurs mutables avec la sémantique de référence.

ref expression

Notes

Pour créer une cellule de référence qui encapsule la valeur, utilisez l'opérateur ref. Vous pouvez ensuite modifier la valeur sous-jacente, car elle est mutable.

Une cellule de référence contient une valeur réelle, et pas uniquement une adresse. Lorsque vous créez une cellule de référence à l'aide de l'opérateur ref, vous créez une copie de la valeur sous-jacente en tant que valeur mutable encapsulée.

Vous pouvez déréférencer une cellule de référence à l'aide de l'opérateur ! (bang).

L'exemple de code suivant illustre la déclaration et l'utilisation de cellules de référence.

// 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

Le résultat est 50.

Les cellules de référence sont des instances du type d'enregistrement générique Ref qui est déclaré comme suit.

type Ref<'a> =
    { mutable contents: 'a }

Le type 'a ref (affiché par le compilateur et IntelliSense dans l'IDE) est un synonyme de Ref<'a> (définition sous-jacente).

L'opérateur ref crée une cellule de référence. Le code suivant est la déclaration de l'opérateur ref.

let ref x = { contents = x }

Le tableau suivant répertorie les fonctionnalités disponibles sur la cellule de référence.

Opérateur, membre ou champ

Description

Type

Définition

! (opérateur de déréférence)

Retourne la valeur sous-jacente.

'a ref -> 'a

let (!) r = r.contents

:= (opérateur d'assignation)

Modifie la valeur sous-jacente.

'a ref -> 'a -> unit

let (:=) r x = r.contents <- x

ref (opérateur)

Encapsule une valeur dans une nouvelle cellule de référence.

'a -> 'a ref

let ref x = { contents = x }

Value (propriété)

Obtient ou définit la valeur sous-jacente.

unit -> 'a

member x.Value = x.contents

contents (champ d'enregistrement)

Obtient ou définit la valeur sous-jacente.

'a

let ref x = { contents = x }

Vous pouvez accéder à la valeur sous-jacente de plusieurs façons. La valeur retournée par l'opérateur de déréférence (!) n'est pas une valeur assignable. Par conséquent, si vous modifiez la valeur sous-jacente, vous devez utiliser à la place l'opérateur d'assignation (:=).

La propriété Value et le champ contents sont des valeurs assignables. Par conséquent, vous pouvez les utiliser pour accéder ou modifier la valeur sous-jacente, comme indiqué dans le code suivant.

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)

La sortie est la suivante.

10
10
11
12

Le champ contents est fourni à des fins de compatibilité avec d'autres versions de ML et produit un avertissement au cours de la compilation. Pour désactiver l'avertissement, utilisez l'option de compilateur --mlcompatibility. Pour plus d'informations, consultez Options du compilateur (F#).

Exemple

Le code suivant illustre l'utilisation de cellules de référence dans le cadre du passage de paramètres. Le type Incrementor comprend une méthode Increment qui accepte un paramètre qui inclut byref dans le type de paramètre. byref dans le type de paramètre indique que les appelants doivent passer une cellule de référence ou l'adresse d'une variable normale du type spécifié, dans ce cas int. Le code restant illustre comment appeler Increment avec ces deux types d'arguments, et indique l'utilisation de l'opérateur ref sur une variable pour créer une cellule de référence (ref myDelta1). Il montre ensuite l'utilisation de l'opérateur d'adresse (&) pour générer un argument approprié. Enfin, la méthode Increment est appelée au moyen d'une cellule de référence déclarée à l'aide d'une liaison let. La dernière ligne de code illustre l'utilisation de l'opérateur ! pour déréférencer la cellule de référence pour l'impression.

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

Pour plus d'informations sur le passage par référence, consultez Paramètres et arguments (F#).

Notes

Les programmeurs C# doivent savoir que ref ne fonctionne pas de la même façon en F# et en C#.Par exemple, l'utilisation de ref lorsque vous passez un argument n'a pas le même effet en F# et en C#.

Cellules de référence et variables mutables

Les cellules de référence et les variables mutables peuvent souvent être utilisées dans les mêmes situations. Toutefois, dans certaines situations, les variables mutables ne peuvent pas être utilisées ; dans ce cas, vous devez utiliser une cellule de référence à la place. En général, il est conseillé de privilégier les variables mutables lorsqu'elles sont acceptées par le compilateur. Toutefois, dans les expressions qui génèrent des clôtures, le compilateur indique que vous ne pouvez pas utiliser de variables mutables. Les clôtures sont des fonctions locales générées par certaines expressions F#, telles que des expressions lambda, des expressions de séquence, des expressions de calcul et des fonctions curryfiées qui utilisent des arguments partiellement appliqués. Les clôtures générées par ces expressions sont stockées pour une évaluation ultérieure. Ce processus n'est pas compatible avec les variables mutables. Par conséquent, si vous avez besoin d'un état mutable dans une telle expression, vous devez utiliser des cellules de référence. Pour plus d'informations sur les clôtures, consultez Clôtures (F#).

L'exemple de code suivant illustre le scénario dans lequel vous devez utiliser une cellule de référence.

// 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
    }

Dans le code précédent, la cellule de référence finished est incluse dans l'état local ; autrement dit, les variables qui se trouvent dans la clôture sont créées et utilisées entièrement dans l'expression, dans ce cas une expression de séquence. Considérez ce qui se produit lorsque les variables ne sont pas locales. Les clôtures peuvent également accéder à l'état non local, mais lorsque cela se produit, les variables sont copiées et stockées par valeur. Ce processus est appelé sémantique de valeur. Cela signifie que les valeurs au moment de la copie sont stockées, et que toutes les modifications suivantes apportées aux variables ne sont pas reflétées. Si vous souhaitez suivre les modifications de variables non locales ou, en d'autres termes, si vous avez besoin d'une clôture qui interagit avec l'état non local en utilisant la sémantique de référence, vous devez utiliser une cellule de référence.

Les exemples de code suivants illustrent l'utilisation de cellules de référence dans les clôtures. Dans ce cas, la fermeture résulte de l'application partielle d'arguments de fonction.

// 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)

Voir aussi

Référence

Référence des symboles et opérateurs (F#)

Concepts

Paramètres et arguments (F#)

Autres ressources

Référence du langage F#