Compartilhar via


Células de referência (F#)

Células de referência são locais de armazenamento que permitem criar valores mutáveis com semântica de referência.

ref expression

Comentários

Você usa o operador ref antes de um valor para criar uma nova célula de referência que encapsule o valor. Em seguida, é possível alterar o valor subjacente porque ele é mutável.

Uma célula de referência mantém um valor real. Não é apenas um endereço. Ao criar uma célula de referência usando o operador ref, você cria uma cópia do valor subjacente como um valor mutável encapsulado.

Você pode desreferenciar uma célula de referência usando o operador ! (bang).

O exemplo de código a seguir ilustra a declaração e o uso das células de referência.

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

A saída é 50.

As células de referência são instâncias do tipo de registro genérico Ref, declarado da maneira a seguir.

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

O tipo 'a ref é um sinônimo de Ref<'a>. O compilador e o IntelliSense no IDE exibem o primeiro para esse tipo, mas a definição subjacente é do último.

O operador ref cria uma nova célula de referência. O código a seguir é a declaração do operador ref.

let ref x = { contents = x }

A tabela a seguir mostra os recursos disponíveis na célula de referência.

Operador, membro ou campo

Descrição

Tipo

Definição

! (operador de desreferência)

Retorna o valor subjacente.

'a ref -> 'a

let (!) r = r.contents

:= (operador de atribuição)

Altera o valor subjacente.

'a ref -> 'a -> unit

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

ref (operador)

Encapsula um valor em uma nova célula de referência.

'a -> 'a ref

let ref x = { contents = x }

Value (propriedade)

Obtém ou define o valor subjacente.

unit -> 'a

member x.Value = x.contents

contents (campo de registro)

Obtém ou define o valor subjacente.

'a

let ref x = { contents = x }

Existem várias maneiras de acessar o valor subjacente. O valor retornado pelo operador de desreferência (!) não é um valor atribuível. Portanto, se estiver modificando o valor subjacente, você deverá usar o operador de atribuição (:=) em vez disso.

A propriedade Value e o campo contents são valores atribuíveis. Portanto, é possível usá-los para acessar ou alterar o valor subjacente, conforme mostrado no código a seguir.

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)

A saída é a seguinte.

10
10
11
12

O campo contents é fornecido para compatibilidade com outras versões do ML e produzirá um aviso durante a compilação. Para desabilitar o aviso, use a opção do compilador --mlcompatibility. Para obter mais informações, consulte Opções do compilador (F#).

Exemplo

O código a seguir ilustra o uso das células de referência na passagem de parâmetro. O tipo Incrementor tem um método Increment que utiliza um parâmetro que inclui byref no tipo de parâmetro. O byref no tipo de parâmetro indica que os chamadores devem passar uma célula de referência ou o endereço de uma variável típica do tipo especificado, neste caso, int. O restante do código ilustra como chamar Increment com esses tipos de argumentos e mostra o uso do operador ref operador em uma variável para criar uma célula de referência (ref myDelta1). Em seguida, ele mostra o uso do operador address-of (&) para gerar um argumento apropriado. Por fim, o método Increment é chamado novamente usando-se uma célula de referência declarada com uma associação let. A linha final do código demonstra o uso do operador ! para desreferenciar a célula de referência para impressão.

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

Para obter mais informações sobre como passar referência, consulte Parâmetros e argumentos (F#).

Dica

Os programadores do C# devem saber que ref funciona de maneira diferente no F# em relação ao C#.Por exemplo, o uso de ref quando você passa um argumento não tem o mesmo efeito no F# como ele faz no C#.

Células de referência x variáveis mutáveis

As células de referência e as variáveis mutáveis normalmente podem ser usadas nas mesmas situações. No entanto, existem algumas situações nas quais variáveis mutáveis não podem ser usadas, e você deve usar uma célula de referência em seu lugar. Em geral, você deve preferir variáveis mutáveis onde elas são aceitas pelo compilador. No entanto, em expressões que geram fechamentos, o compilador reportará que não é possível usar variáveis mutáveis. Fechamentos são funções locais geradas por determinadas expressões do F#, como expressões lambda, expressões de sequência, expressões de computação e funções curried que usam argumentos aplicados parcialmente. Os fechamentos gerados por essas expressões são armazenados para avaliação posterior. Esse processo não é compatível com variáveis mutáveis. Portanto, se precisar de um estado mutável em uma expressão assim, você precisará usar células de referência. Para obter mais informações sobre fechamentos, consulte Fechamentos (F#).

O exemplo de código a seguir demonstra o cenário em que você deve usar uma célula de referência.

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

No código anterior, a célula de referência finished está incluída no estado local, ou seja, variáveis que estão no fechamento são criadas e usadas inteiramente dentro da expressão, neste caso, uma expressão de sequência. Considere o que ocorre quando as variáveis não são locais. Os fechamentos também podem acessar estado não local, mas quando isso ocorre, as variáveis são copiadas e armazenadas por valor. Esse processo é conhecido como semântica de valor. Isso significa que os valores no momento da cópia são armazenados e todas as alterações subsequentes feitas nas variáveis não são refletidas. Se quiser acompanhar as alterações de variáveis não locais, ou, em outras palavras, se precisar de um fechamento que interaja com o estado não local usando semântica de referência, você deverá usar uma célula de referência.

Os exemplos de código a seguir demonstram o uso das células de referência em fechamentos. Nesse caso, o fechamento é resultante da aplicação parcial dos argumentos de função.

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

Consulte também

Referência

Referência de símbolos e operadores (F#)

Conceitos

Parâmetros e argumentos (F#)

Outros recursos

Referência da linguagem F#