共用方式為


參照儲存格 (F#)

「參考儲存格」(Reference Cell) 是可讓您以參考語意建立可變值的儲存位置。

ref expression

備註

您可以在值的前面使用 ref 運算子,建立可封裝值的新參考儲存格。由於基礎值是可變的,因此您接著可以變更這個基礎值。

參考儲存格不只是地址,還會保存實際值。當您使用 ref 運算子建立參考儲存格時,會建立基礎值的複本做為已封裝的可變值。

您可以使用 ! (驚嘆號) 運算子來取值 (Dereference) 參考儲存格。

下列程式碼範例將示範參考儲存格的宣告和用法。

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

輸出為 50。

參考儲存格為 Ref 泛型記錄型別的執行個體,其宣告如下。

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

'a ref 型別是 Ref<'a> 的同義字。IDE 中的編譯器和 IntelliSense 會對此型別顯示前者,但基礎定義則為後者。

ref 運算子會建立新的參考儲存格。下列程式碼為 ref 運算子的宣告。

let ref x = { contents = x }

下表顯示參考儲存格上的可用功能。

運算子、成員或欄位

描述

型別

定義

! (取值運算子)

傳回基礎值。

'a ref -> 'a

let (!) r = r.contents

:= (指派運算子)

變更基礎值。

'a ref -> 'a -> unit

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

ref (運算子)

將值封裝至新的參考儲存格。

'a -> 'a ref

let ref x = { contents = x }

Value (屬性)

取得或設定基礎值。

unit -> 'a

member x.Value = x.contents

contents (記錄欄位)

取得或設定基礎值。

'a

let ref x = { contents = x }

有數個方式可以存取基礎值。取值運算子 (!) 傳回的值不是可指派的值。因此如果您要修改基礎值,則必須改用指派運算子 (:=)。

Value 屬性和 contents 欄位都是可指派的值。因此,您可以使用它們來存取或變更基礎值,如下列程式碼所示。

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)

輸出如下。

10
10
11
12

contents 欄位是針對與其他 ML 版本相容而提供,而且會在編譯期間產生警告。若要停用這個警告,請使用 --mlcompatibility 編譯器選項。如需詳細資訊,請參閱編譯器選項 (F#)

範例

下列程式碼將示範如何將參考儲存格用於參數傳遞。Incrementor 型別的方法 Increment 會接受參數型別中包含 byref 的參數。參數型別中的 byref 表示呼叫端必須傳遞參考儲存格或指定之型別的一般變數位址,該型別在此情況下為 int。其餘的程式碼示範如何搭配這兩種引數來呼叫 Increment,以及示範如何在變數上使用 ref 運算子來建立參考儲存格 (ref myDelta1)。接著,程式碼會示範如何使用傳址運算子 (&) 來產生適當引數。最後,再使用透過 let 繫結所宣告的參考儲存格,再次呼叫 Increment 方法。最後一行程式碼示範如何使用 ! 運算子來取值參考儲存格供列印之用。

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

如需如何以傳址方式傳遞的詳細資訊,請參閱參數和引數 (F#)

注意事項注意事項

C# 程式設計人員應該知道 F# 中 ref 的運作方式不同於 C#。例如,傳遞引數時,F# 與 C# 中的 ref 有不同的效果。

參考儲存格與可變變數

參考儲存格和可變變數通常都可用於相同的情況。不過,有些情況下可變變數無法使用,這時必須改用參考儲存格。一般而言,在編譯器接受可變變數的情況下,使用可變變數是較常見的作法。不過,在產生封閉區段的運算式中,編譯器就會報告無法使用可變變數。「封閉區段」(Closure) 是特定 F# 運算式所產生的區域函式,例如 Lambda 運算式、序列運算式、計算運算式,以及使用部分已套用之引數的局部調用函式。這些運算式所產生的封閉區段會儲存供稍後評估之用。這個程序與可變變數不相容。因此,如果在這類運算式中需要可變狀態,則必須使用參考儲存格。如需封閉區段的詳細資訊,請參閱<封閉區段 (F#)>。

下列程式碼範例將示範必須使用參考儲存格的案例。

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

在前述的程式碼中,參考儲存格 finished 包含於區域狀態中,也就是位於封閉區段中的變數只在運算式 (在此情況下為序列運算式) 內建立及使用。試想當變數為非區域變數時會發生什麼狀況。封閉區段也可以存取非區域性狀態,但發生這種狀況時,變數會以傳值方式複製及儲存。這個程序稱為「實值語意」(Value Semantics)。這表示會儲存複製時的值,而且不會反映變數的任何後續變更。如果您要追蹤非區域變數的變更,換句話說,需要透過「參考語意」(Reference Semantics) 與非區域狀態互動的封閉區段時,則必須使用參考儲存格。

在下列程式碼範例中,將示範參考儲存格在封閉區段內的用法。在此情況下,封閉區段會因套用部分函式引數而有所不同。

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

請參閱

參考

符號和運算子參考 (F#)

概念

參數和引數 (F#)

其他資源

F# 語言參考