Estructuras
Una estructura es un tipo de objeto compacto que puede ser más eficaz que una clase en tipos que tienen una pequeña cantidad de datos y un comportamiento simple.
Sintaxis
[ 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
Comentarios
Las estructuras son tipos de valor, lo que significa que se almacenan directamente en la pila o, cuando se utilizan como campos o tipo de elementos de la matriz, alineados en el tipo primario. A diferencia de los registros y las clases, las estructuras tienen semántica de paso por valor. Esto significa que son útiles principalmente para pequeños agregados de datos a los que accede y que se copian con frecuencia.
En la sintaxis anterior, se muestran dos formularios. La primera no es la sintaxis ligera; sin embargo, se utiliza frecuentemente porque, cuando se usan las palabras clave struct
y end
, se puede omitir el atributo StructAttribute
, que aparece en el segundo formulario. StructAttribute
se puede abreviar como Struct
.
El type-definition-elements-and-members en la sintaxis anterior, representa definiciones y declaraciones de miembros. Las estructuras pueden tener constructores y campos mutables e inmutables, y pueden declarar implementaciones de interfaces y miembros. Para más información, consulte Miembros.
Las estructuras no pueden participar en la herencia, no pueden contener enlaces let
ni do
, y no puede contener de forma recursiva campos de su propio tipo (aunque pueden contener celdas de referencia que hagan referencia a su propio tipo).
Dado que las estructuras no permiten enlaces let
, debe declarar campos en estructuras mediante el uso de la palabra clave val
. La palabra clave val
define un campo y su tipo, pero no permite la inicialización. En su lugar, las declaraciones val
se inicializan en cero o null. Por este motivo, las estructuras que tienen un constructor implícito (es decir, los parámetros que se proporcionan inmediatamente después del nombre de la estructura en la declaración) requieren que las declaraciones val
se anoten con el atributo DefaultValue
. Las estructuras que tienen un constructor definido todavía admiten la inicialización en cero. Por lo tanto, el atributo DefaultValue
es una declaración de que ese valor cero es válido para el campo. Los constructores implícitos de las estructuras no realizan ninguna acción porque los enlaces let
y do
no están permitidos en el tipo, pero los valores de parámetro de constructor implícito pasados están disponibles como campos privados.
Los constructores explícitos podrían implicar la inicialización de los valores de campo. Cuando se tiene una estructura con un constructor explícito, se admite la inicialización en cero; sin embargo, no se utiliza el atributo DefaultValue
en las declaraciones val
porque entra en conflicto con el constructor explícito. Para obtener más información sobre las declaraciones val
, consulte Campos explícitos: La palabra clave val
.
Los atributos y modificadores de accesibilidad están permitidos en las estructuras y siguen las mismas reglas que las de otros tipos. Para más información, consulte Atributos y Control de acceso.
Los siguientes ejemplos de código ilustran las definiciones de la estructura.
// 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
Estructuras ByRefLike
Puede definir sus propias estructuras que pueden cumplir la semántica similar a byref
: consulte Byrefs para obtener más información. Esto se realiza a través del 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
no implica Struct
. Ambos deben estar presentes en el tipo.
Una estructura «similar abyref
» en F# es un tipo de valor enlazado a la pila. Nunca se asigna en el montón administrado. Una estructura similiar a byref
es útil para la programación de alto rendimiento, ya que se aplica con un conjunto de comprobaciones seguras sobre la duración y la no captura. Las reglas son:
- Se pueden usar como parámetros de función, parámetros de método, variables locales, devoluciones de método.
- No pueden ser miembros estáticos o de instancia de una clase o una estructura normal.
- No se pueden capturar mediante ninguna construcción de cierre (métodos
async
o expresiones lambda). - No se pueden usar como parámetro genérico.
Aunque estas reglas restringen fuertemente el uso, lo hacen para cumplir la promesa de informática de alto rendimiento de una manera segura.
Estructuras ReadOnly
Puede anotar estructuras con el atributo IsReadOnlyAttribute. Por ejemplo:
[<IsReadOnly; Struct>]
type S(count1: int, count2: int) =
member x.Count1 = count1
member x.Count2 = count2
IsReadOnly
no implica Struct
. Debe agregar ambos para tener una estructura IsReadOnly
.
El uso de este atributo emite metadatos que permiten que F# y C# sepan tratarlos como inref<'T>
y in ref
, respectivamente.
La definición de un valor mutable dentro de una estructura readonly genera un error.
Registros de estructura y uniones discriminadas
Puede representar registros y uniones discriminadas como estructuras con el atributo [<Struct>]
. Consulte cada artículo para obtener más información.