Estruturas

A estrutura é um tipo de objeto compacto que pode ser mais eficiente do que uma classe para tipos que tenham uma pequena quantidade de dados e comportamento simples.

Sintaxe

[ attributes ]
type [accessibility-modifier] type-name =
    struct
        type-definition-elements-and-members
    end
// or
[ attributes ]
[<StructAttribute>]
type [accessibility-modifier] type-name =
    type-definition-elements-and-members

Comentários

Estruturas são tipos de valor, o que significa que elas são armazenadas diretamente na pilha ou, quando forem usadas como campos ou elementos de matriz, são embutidas no tipo pai. Ao contrário de classes e registros, as estruturas têm semântica de passar-por-valor. Isso significa que elas são úteis principalmente para pequenos agregados de dados que são acessados e copiados com frequência.

Na sintaxe anterior, duas formas são mostradas. A primeira não é a sintaxe leve, mas ela é usada com frequência, pois quando você usa as palavras-chave struct e end, é possível omitir o atributo StructAttribute, que aparece na segunda forma. É possível abreviar StructAttribute como apenas Struct.

O type-definition-elements-and-members na sintaxe anterior representa declarações e definições de membros. As estruturas podem ter construtores e campos mutáveis e imutáveis e podem declarar membros e implementações de interface. Para obter mais informações, confira Membros.

As estruturas não podem participar da herança, não podem conter associações let ou do e não podem conter recursivamente campos de seu próprio tipo (embora possam conter células de referência que fazem referência ao seu próprio tipo).

Como as estruturas não permitem associações let, você deve declarar campos em estruturas usando a palavra-chave val. A palavra-chave val define um campo e seu tipo, mas não permite inicialização. Em vez disso, as declarações val são inicializadas como zero ou nulo. Por esse motivo, as estruturas que possuem um construtor implícito (ou seja, parâmetros que são fornecidos imediatamente após o nome da estrutura na declaração) requerem que declarações val sejam anotadas com o atributo DefaultValue. As estruturas que tenham um construtor definido ainda oferecem suporte à inicialização como zero. Portanto, o atributo DefaultValue é uma declaração de que um valor zero é válido para o campo. Construtores implícitos para estruturas não executam nenhuma ação, pois as associações let e do não são permitidas no tipo, mas os valores de parâmetro construtor implícito passados estão disponíveis como campos particulares.

Construtores explícitos podem envolver inicialização de valores do campo. Quando você tem uma estrutura que possui um construtor explícito, ela ainda suporta inicialização como zero; no entanto, você não usa o atributo DefaultValue nas declarações val, pois ela entra em conflito com o construtor explícito. Para obter mais informações sobre declarações val, consulte Campos explícitos: a palavra-chave val.

Modificadores de atributos e de acessibilidade são permitidos em estruturas e seguem as mesmas regras dos outros tipos. Para obter mais informações, consulte Atributos e Controle de acesso.

Os exemplos de código a seguir ilustram definições de estrutura.

// In Point3D, three immutable values are defined.
// x, y, and z will be initialized to 0.0.
type Point3D =
    struct
        val x: float
        val y: float
        val z: float
    end

// In Point2D, two immutable values are defined.
// It also has a member which computes a distance between itself and another Point2D.
// Point2D has an explicit constructor.
// You can create zero-initialized instances of Point2D, or you can
// pass in arguments to initialize the values.
type Point2D =
    struct
        val X: float
        val Y: float
        new(x: float, y: float) = { X = x; Y = y }

        member this.GetDistanceFrom(p: Point2D) =
            let dX = (p.X - this.X) ** 2.0
            let dY = (p.Y - this.Y) ** 2.0

            dX + dY |> sqrt
    end

Structs ByRefLike

Você pode definir seus próprios structs que podem aderir a semânticas semelhantes a byref: consulte Byrefs para obter mais informações. Isso é feito com o atributo IsByRefLikeAttribute:

open System
open System.Runtime.CompilerServices

[<IsByRefLike; Struct>]
type S(count1: Span<int>, count2: Span<int>) =
    member x.Count1 = count1
    member x.Count2 = count2

IsByRefLike não implica Struct. Ambos devem estar presentes no tipo.

Um struct "byref-like" em F# é um tipo de valor associado à pilha. Ele nunca é alocado no heap gerenciado. Um struct byref-like é útil para programação de alto desempenho, pois é aplicado com um conjunto de verificações robustas sobre vida útil e não captura. As regras são:

  • Eles podem ser usados como parâmetros de função, parâmetros de método, variáveis locais, retorna método.
  • Eles não podem ser membros estáticos ou de instância de uma classe ou struct normal.
  • Eles não podem ser capturados por nenhum constructo de fechamento (métodos async ou expressões lambda).
  • Eles não podem ser usados como um parâmetro genérico.

Embora essas regras restrinjam fortemente o uso, elas o fazem para cumprir a promessa de computação de alto desempenho de maneira segura.

Structs ReadOnly

Você pode anotar structs com o atributo IsReadOnlyAttribute. Por exemplo:

[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
    member x.Count1 = count1
    member x.Count2 = count2

IsReadOnly não implica Struct. Você deve adicionar ambos para ter um struct IsReadOnly.

O uso desse atributo emite metadados que permitem que F# e C# saibam tratá-lo como inref<'T> e in ref, respectivamente.

Definir um valor mutável dentro de um struct readOnly produz um erro.

Registros de struct e uniões discriminadas

Você pode representar Registros e Uniões discriminadas como estruturas com o atributo [<Struct>]. Veja cada artigo para saber mais.

Confira também