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.