Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Las extensiones de tipo (también denominadas aumentos) son una familia de características que permiten agregar nuevos miembros a un tipo de objeto definido previamente. Las tres características son:
- Extensiones de tipos intrínsecos
- Extensiones de tipo opcionales
- Métodos de extensión
Cada uno se puede usar en diferentes escenarios y tiene diferentes inconvenientes.
Sintaxis
// Intrinsic and optional extensions
type typename with
member self-identifier.member-name =
body
...
// Extension methods
open System.Runtime.CompilerServices
[<Extension>]
type Extensions() =
[<Extension>]
static member extension-name (ty: typename, [args]) =
body
...
Extensiones de tipos intrínsecos
Una extensión de tipo intrínseco es una extensión de tipo que extiende un tipo definido por el usuario.
Las extensiones de tipo intrínseco deben definirse en el mismo archivo y en el mismo espacio de nombres o módulo que el tipo que extienden. Cualquier otra definición hará que sean extensiones de tipo opcionales.
Las extensiones de tipo intrínsecas a veces son una manera más limpia de separar la funcionalidad de la declaración de tipo. En el ejemplo siguiente se muestra cómo definir una extensión de tipo intrínseco:
namespace Example
type Variant =
| Num of int
| Str of string
module Variant =
let print v =
match v with
| Num n -> printf "Num %d" n
| Str s -> printf "Str %s" s
// Add a member to Variant as an extension
type Variant with
member x.Print() = Variant.print x
El uso de una extensión de tipo le permite separar cada una de las siguientes opciones:
- Declaración de un
Varianttipo - Funcionalidad para imprimir la
Variantclase en función de su "forma" - Una manera de acceder a la funcionalidad de impresión con notación de estilo
.de objeto
Esta es una alternativa a definir todo como miembro en Variant. Aunque no es un enfoque intrínsecamente mejor, puede ser una representación más limpia de la funcionalidad en algunas situaciones.
Las extensiones de tipo intrínseco se compilan como miembros del tipo que aumentan y aparecen en el tipo cuando el tipo se examina mediante reflexión.
Extensiones de tipo opcionales
Una extensión de tipo opcional es una extensión que aparece fuera del módulo original, el espacio de nombres o el ensamblado del tipo que se va a extender.
Las extensiones de tipo opcionales son útiles para extender un tipo que no haya definido usted mismo. Por ejemplo:
module Extensions
type IEnumerable<'T> with
/// Repeat each element of the sequence n times
member xs.RepeatElements(n: int) =
seq {
for x in xs do
for _ in 1 .. n -> x
}
Ahora puede acceder RepeatElements como si fuera miembro de IEnumerable<T> siempre que el Extensions módulo se abra en el ámbito en el que trabaja.
Las extensiones opcionales no aparecen en el tipo extendido cuando se examina mediante reflexión. Las extensiones opcionales deben estar en módulos y solo están en el ámbito cuando el módulo que contiene la extensión está abierto o está en el ámbito.
Los miembros de extensión opcionales se compilan en miembros estáticos para los que la instancia de objeto se pasa implícitamente como primer parámetro. Sin embargo, actúan como si fueran miembros de instancia o miembros estáticos según cómo se declaran.
Los miembros de extensión opcionales tampoco son visibles para los consumidores de C# o Visual Basic. Solo se pueden consumir en otro código de F#.
Limitación genérica de extensiones de tipo intrínsecas y opcionales
Es posible declarar una extensión de tipo en un tipo genérico en el que se restringe la variable de tipo. El requisito es que la restricción de la declaración de extensión coincide con la restricción del tipo declarado.
Sin embargo, incluso cuando las restricciones coinciden entre un tipo declarado y una extensión de tipo, es posible que el cuerpo de un miembro extendido infiere una restricción que impone un requisito diferente en el parámetro de tipo que el tipo declarado. Por ejemplo:
open System.Collections.Generic
// NOT POSSIBLE AND FAILS TO COMPILE!
//
// The member 'Sum' has a different requirement on 'T than the type IEnumerable<'T>
type IEnumerable<'T> with
member this.Sum() = Seq.sum this
No hay ninguna manera de obtener este código para trabajar con una extensión de tipo opcional:
- Como es, el
Summiembro tiene una restricción diferente en'T(static member get_Zeroystatic member (+)) que la extensión de tipo define. - Modificar la extensión de tipo para que tenga la misma restricción que
Sumya no coincidirá con la restricción definida enIEnumerable<'T>. - Cambiar
member this.Sumamember inline this.Sumdará un error que indica que las restricciones de tipo no coinciden.
Lo que se desea son métodos estáticos que "flotan en el espacio" y se pueden presentar como si ampliaran un tipo. Aquí es donde los métodos de extensión son necesarios.
Métodos de extensión
Por último, los métodos de extensión (a veces denominados "miembros de extensión de estilo de C#") se pueden declarar en F# como un método de miembro estático en una clase.
Los métodos de extensión son útiles para cuando se desean definir extensiones en un tipo genérico que restringirá la variable de tipo. Por ejemplo:
namespace Extensions
open System.Collections.Generic
open System.Runtime.CompilerServices
[<Extension>]
type IEnumerableExtensions =
[<Extension>]
static member inline Sum(xs: IEnumerable<'T>) = Seq.sum xs
Cuando se usa, este código hará que aparezca como si Sum se define en IEnumerable<T>, siempre Extensions que se haya abierto o esté en el ámbito.
Para que la extensión esté disponible para VB.NET código, se requiere un adicional ExtensionAttribute en el nivel de ensamblado:
module AssemblyInfo
open System.Runtime.CompilerServices
[<assembly:Extension>]
do ()
Otros comentarios
Las extensiones de tipo también tienen los atributos siguientes:
- Se puede ampliar cualquier tipo al que se pueda acceder.
- Las extensiones de tipo intrínsecas y opcionales pueden definir cualquier tipo de miembro, no solo métodos. Por lo tanto, las propiedades de extensión también son posibles, por ejemplo.
- El
self-identifiertoken de la sintaxis representa la instancia del tipo que se invoca, al igual que los miembros ordinarios. - Los miembros extendidos pueden ser miembros estáticos o de instancia.
- Las variables de tipo de una extensión de tipo deben coincidir con las restricciones del tipo declarado.
También existen las siguientes limitaciones para las extensiones de tipo:
- Las extensiones de tipo no admiten métodos virtuales o abstractos.
- Las extensiones de tipo no admiten métodos de invalidación como aumentos.
- Las extensiones de tipo no admiten parámetros de tipo resueltos estáticamente.
- Las extensiones de tipo opcionales no admiten constructores como aumentos.
- Las extensiones de tipo no se pueden definir en abreviaturas de tipo.
- Las extensiones de tipo no son válidas para
byref<'T>(aunque se pueden declarar). - Las extensiones de tipo no son válidas para los atributos (aunque se pueden declarar).
- Puede definir extensiones que sobrecargan otros métodos del mismo nombre, pero el compilador de F# da preferencia a los métodos que no son de extensión si hay una llamada ambigua.
Por último, si existen varias extensiones de tipo intrínsecas para un tipo, todos los miembros deben ser únicos. Para las extensiones de tipo opcionales, los miembros de diferentes extensiones de tipo al mismo tipo pueden tener los mismos nombres. Los errores de ambigüedad solo se producen si el código de cliente abre dos ámbitos diferentes que definen los mismos nombres de miembro.