Referenzzellen (F#)

Referenzzellen sind Speicherorte, die Ihnen ermöglichen, änderbare Werte mit Verweissemantik zu erstellen.

ref expression

Hinweise

Sie verwenden den Operator ref vor einem Wert, um eine neue Referenzzelle zu erstellen, die den Wert kapselt. Anschließend können Sie den zugrunde liegenden Wert ändern, da er änderbar ist.

Eine Referenzzelle enthält einen tatsächlichen Wert, sie ist nicht lediglich eine Adresse. Wenn Sie mit dem Operator ref eine Referenzzelle erstellen, erstellen Sie eine Kopie des zugrunde liegenden Werts als gekapselten änderbaren Wert.

Mit dem Operator ! (Bang) können Sie eine Referenzzelle dereferenzieren.

Im folgenden Codebeispiel werden die Deklaration und Verwendung von Referenzzellen veranschaulicht.

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

Die Ausgabe lautet 50.

Referenzzellen sind Instanzen des generischen Ref-Datensatztyps, der wie folgt deklariert wird.

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

Der Typ 'a ref ist ein Synonym für Ref<'a>. Der Compiler und IntelliSense in der IDE zeigen erstere Version für diesen Typ an, jedoch ist letztere Version die zugrunde liegende Definition.

Mit dem Operator ref wird eine neue Referenzzelle erstellt. Der folgende Code ist die Deklaration des Operators ref.

let ref x = { contents = x }

Die folgende Tabelle enthält die Funktionen, die für die Referenzzelle verfügbar sind.

Operator, Member oder Feld

Beschreibungen

Typ

Definition

! (Dereferenzierungsoperator)

Gibt den zugrunde liegenden Wert zurück.

'a ref -> 'a

let (!) r = r.contents

:= (Zuweisungsoperator)

Ändert den zugrunde liegenden Wert.

'a ref -> 'a -> unit

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

ref (Operator)

Kapselt einen Wert in einer neuen Referenzzelle.

'a -> 'a ref

let ref x = { contents = x }

Value (Eigenschaft)

Ruft den zugrunde liegenden Wert ab oder legt diesen fest.

unit -> 'a

member x.Value = x.contents

contents (Datensatzfeld)

Ruft den zugrunde liegenden Wert ab oder legt diesen fest.

'a

let ref x = { contents = x }

Es gibt mehrere Möglichkeiten, auf den zugrunde liegenden Wert zuzugreifen. Der vom Dereferenzierungsoperator (!) zurückgegebene Wert ist kein zuweisbarer Wert. Wenn Sie den zugrunde liegenden Wert ändern, müssen Sie daher stattdessen den Zuweisungsoperator (:=) verwenden.

Sowohl die Value-Eigenschaft als auch das contents-Feld sind zuweisbare Werte. Daher können Sie diese verwenden, um auf den zugrunde liegenden Wert zuzugreifen oder diesen zu ändern, wie im folgenden Code gezeigt.

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)

Die Ausgabe lautet wie folgt.

10
10
11
12

Das Feld contents wird für die Kompatibilität mit anderen Versionen von ML bereitgestellt und gibt während der Kompilierung eine Warnung aus. Verwenden Sie die --mlcompatibility-Compileroption, um die Warnung zu deaktivieren. Weitere Informationen finden Sie unter Compileroptionen (F#).

Beispiel

Im folgenden Code wird die Verwendung von Referenzzellen beim Übergeben von Parametern veranschaulicht. Der Incrementor-Typ verfügt über die Methode Increment, die einen Parameter akzeptiert, der im Parametertyp byref enthält. byref im Parametertyp gibt an, dass Aufrufer eine Referenzzelle oder die Adresse einer typischen Variablen des angegebenen Typs übergeben müssen, in diesem Fall int. Im restlichen Code wird veranschaulicht, wie Increment mit beiden dieser Typen von Argumenten aufgerufen wird, und es wird die Verwendung des Operators ref für eine Variable zum Erstellen einer Referenzzelle (ref myDelta1) gezeigt. Anschließend wird die Verwendung des address-of-Operators (&) zum Generieren eines entsprechenden Arguments veranschaulicht. Schließlich wird erneut die Increment-Methode mithilfe einer Referenzzelle aufgerufen, die mit einer let-Bindung deklariert wird. Die letzte Codezeile veranschaulicht die Verwendung des Operators ! zum Dereferenzieren der Referenzzelle für den Druck.

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

Weitere Informationen zum Übergeben als Verweis finden Sie unter Parameter und Argumente (F#).

Tipp

C#-Programmierer sollten wissen, dass sich die Funktionsweise von ref in F# von der Funktionsweise in C# unterscheidet. Beispielsweise hat beim Übergeben eines Arguments die Verwendung von ref in F# andere Auswirkungen als in C#.

Referenzzellen oderÄnderbare Variablen

Referenzzellen und änderbare Variablen können oft in den gleichen Situationen verwendet werden. Es gibt jedoch einige Situationen, in denen änderbare Variablen nicht verwendet werden können und Sie stattdessen eine Referenzzelle verwenden müssen. Im Allgemeinen sollten Sie vorzugsweise änderbare Variablen verwenden, wenn sie vom Compiler akzeptiert werden. In Ausdrücken, die Abschlüsse generieren, meldet der Compiler jedoch, dass Sie keine änderbaren Variablen verwenden dürfen. Abschlüsse sind lokale Funktionen, die von bestimmten F#-Ausdrücken, z. B. Lambda-Ausdrücken, Sequenzausdrücken, Berechnungsausdrücken und Curry-Funktionen, die partiell angewendete Argumente verwenden, generiert werden. Die von diesen Ausdrücken generierten Abschlüsse werden für die spätere Auswertung gespeichert. Dieser Vorgang ist nicht mit änderbaren Variablen kompatibel. Daher müssen Sie Referenzzellen verwenden, wenn Sie in einem solchen Ausdruck einen änderbaren Zustand benötigen. Weitere Informationen zu Abschlüssen finden Sie unter Abschlüsse (F#).

Im folgenden Codebeispiel wird das Szenario veranschaulicht, in dem Sie eine Referenzzelle verwenden müssen.

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

Im vorherigen Code ist die Referenzzelle finished im lokalen Zustand eingeschlossen, das heißt, im Abschluss enthaltene Variablen werden vollständig innerhalb des Ausdrucks, in diesem Fall ein Sequenzausdruck, erstellt und verwendet. Betrachten Sie den Fall, wenn die Variablen nicht lokal sind. Abschlüsse können auch auf den nicht lokalen Zustand zugreifen, jedoch werden in diesem Fall die Variablen nach Wert kopiert und gespeichert. Dieser Vorgang wird als Wertsemantik bezeichnet. Dies bedeutet, dass die Werte zum Zeitpunkt der Kopie gespeichert werden, und spätere Änderungen an den Variablen werden nicht wiedergegeben. Wenn Sie die Änderungen nicht lokaler Variablen nachverfolgen möchten, d. h., wenn Sie einen Abschluss benötigen, der mithilfe von Verweissemantik mit dem nicht lokalen Zustand interagiert, müssen Sie eine Referenzzelle verwenden.

In den folgenden Codebeispielen wird die Verwendung von Referenzzellen in Abschlüssen veranschaulicht. In diesem Fall resultiert der Abschluss aus der partiellen Anwendung von Funktionsargumenten.

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

Siehe auch

Referenz

Symbol- und Operatorenreferenz (F#)

Konzepte

Parameter und Argumente (F#)

Weitere Ressourcen

F#-Sprachreferenz