Compartilhar via


Estruturas

Uma estrutura é um tipo de objeto compacto que pode ser mais eficiente do que uma classe para tipos que têm 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

Observações

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

Na sintaxe anterior, dois formulários são mostrados. A primeira não é a sintaxe leve, mas é usada com frequência porque, quando você usa as palavras-chave e end as struct palavras-chave, você pode omitir o StructAttribute atributo, que aparece na segunda forma. Você pode abreviar StructAttribute para apenas Struct.

A sintaxe type-definition-elements-and-members na sintaxe anterior representa declarações e definições de membro. 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, consulte Membros.

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

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

Construtores explícitos podem envolver a inicialização de valores de campo. Quando você tem uma estrutura que tem um construtor explícito, ela ainda dá suporte à inicialização zero; no entanto, você não usa o DefaultValue atributo nas val declarações porque ele está em conflito com o construtor explícito. Para obter mais informações sobre val declarações, consulte Campos Explícitos: A val Palavra-chave.

Os atributos e modificadores de acessibilidade são permitidos em estruturas e seguem as mesmas regras de 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 à semântica semelhante 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

IsByRefLikenão implica .Struct Ambos devem estar presentes no tipo.

Um struct "byrefsemelhante" em F# é um tipo de valor associado à pilha. Ele nunca é alocado no heap gerenciado. Um byrefstruct semelhante é útil para programação de alto desempenho, pois é imposto com um conjunto de verificações fortes sobre o tempo de vida e a 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, retornos de 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 qualquer construção de fechamento (async métodos 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 IsReadOnlyAttribute atributo. Por exemplo:

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

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

O uso desse atributo emite metadados permitindo 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.

Struct Records and Discriminated Unions

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

Consulte também