Estructuras
Una estructura es un tipo de objeto compacto que puede ser más eficaz que una clase para los 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
Observaciones
Las estructuras son tipos de valor , lo que significa que se almacenan directamente en la pila o, cuando se usan como campos o elementos de matriz, en línea 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 de la sintaxis anterior representa declaraciones y definiciones de miembros. Las estructuras pueden tener constructores y campos mutables e inmutables, y pueden declarar implementaciones de interfaces y miembros. Para obtener más información, vea 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 val las declaraciones, vea Campos explícitos: La val palabra clave.
Los atributos y modificadores de accesibilidad están permitidos en las estructuras y siguen las mismas reglas que las de otros tipos. Para obtener más información, vea Atributos y Access Control.
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 propios structs que puedan cumplir la semántica similar a byref la siguiente: consulte Byrefs para obtener más información. Esto se hace con el IsByRefLikeAttribute atributo :
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.
Un byref struct "-like" en F# es un tipo de valor enlazado a la pila. Nunca se asigna en el montón administrado. Un struct de tipo es útil para la programación de alto rendimiento, ya que se aplica con un conjunto de comprobaciones seguras sobre la duración byref y la no captura. Las reglas son:
- Se pueden usar como parámetros de función, parámetros de método, variables locales y devoluciones de métodos.
- No pueden ser miembros estáticos o de instancia de una clase o struct normal.
- No se pueden capturar mediante ninguna construcción de cierre
async(métodos o expresiones lambda). - No se pueden usar como parámetro genérico.
Aunque estas reglas restringen mucho el uso, lo hacen para cumplir la promesa de informática de alto rendimiento de una manera segura.
Structs readOnly
Puede anotar structs con el IsReadOnlyAttribute atributo . 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 un IsReadOnly struct.
El uso de este atributo emite metadatos que permiten que F# y C# sepan tratarlo como inref<'T> y in ref , respectivamente.
La definición de un valor mutable dentro de una estructura de solo lectura genera un error.
Registros de estructura y uniones discriminadas
Puede representar registros y uniones discriminadas como estructuras con el [<Struct>] atributo . Consulte cada artículo para obtener más información.