Extensiones de tipo (F#)
Las extensiones de tipo permiten agregar nuevos miembros a un tipo de objeto previamente definido.
// Intrinsic extension.
type typename with
member self-identifier.member-name =
body
...
[ end ]
// Optional extension.
type typename with
member self-identifier.member-name =
body
...
[ end ]
Comentarios
Hay dos formas de extensiones de tipo cuyo comportamiento y sintaxis se diferencian ligeramente. Una extensión intrínseca es aquella que aparece en el mismo espacio de nombres o módulo, en el mismo archivo de origen y en el mismo ensamblado (DLL o archivo ejecutable) que el tipo que se extiende. Una extensión opcional es aquella que aparece fuera del módulo, espacio de nombres o ensamblado originales del tipo que se extiende. Las extensiones intrínsecas aparecen en el tipo cuando este se examina mediante reflexión, pero las extensiones opcionales, no. Las extensiones opcionales deben estar en módulos y únicamente se encuentran dentro del ámbito cuando el módulo que las contiene está abierto.
En la sintaxis anterior, typename representa el tipo que se extiende. Se puede extender cualquier tipo al que se pueda tener acceso, pero el nombre de tipo debe ser un nombre de tipo real, no una abreviatura de tipo. Se pueden definir varios miembros en una extensión de tipo. self-identifier representa la instancia del objeto que se invoca, igual que en los miembros ordinarios.
La palabra clave end es opcional en sintaxis ligera.
Los miembros definidos en las extensiones de tipo se pueden utilizar exactamente igual que los demás miembros de un tipo de clase. Como los demás miembros, pueden ser estáticos o miembros de instancia. Estos métodos también se denominan métodos de extensión; las propiedades se denominan propiedades de extensión, y así sucesivamente. Los miembros de extensión opcionales se compilan en miembros estáticos para los que se pasa la instancia de objeto implícitamente como primer parámetro. Sin embargo, se comportan como si fueran miembros de instancia o miembros estáticos según cómo se declaran. Los miembros de extensión implícitos se incluyen como miembros del tipo y se pueden utilizar sin restricciones.
Los métodos de extensión no pueden ser virtuales ni abstractos. Pueden sobrecargar otros métodos del mismo nombre, pero el compilador da preferencia a los métodos que no sean de extensión en el caso de una llamada ambigua.
Si hay varias extensiones de tipo intrínsecas para un tipo, todos los miembros deben ser únicos. Para las extensiones de tipo opcionales, miembros de distintas extensiones de tipo correspondientes al mismo tipo pueden tener nombres iguales. Únicamente se producen errores de ambigüedad si el código de cliente abre dos ámbitos diferentes que definen los mismos nombres de miembro.
En el ejemplo siguiente, un tipo de un módulo tiene una extensión de tipo intrínseca. Para el código de cliente externo al módulo, la extensión de tipo aparece como un miembro normal del tipo en todos los aspectos.
module MyModule1 =
// Define a type.
type MyClass() =
member this.F() = 100
// Define type extension.
type MyClass with
member this.G() = 200
module MyModule2 =
let function1 (obj1: MyModule1.MyClass) =
// Call an ordinary method.
printfn "%d" (obj1.F())
// Call the extension method.
printfn "%d" (obj1.G())
Puede utilizar extensiones de tipo intrínsecas para separar la definición de un tipo en secciones. Esto puede resultar útil para administrar grandes definiciones de tipo; por ejemplo, para mantener el código generado mediante compilador separado del código creado, o para agrupar código creado por personas diferentes o asociado a funcionalidades distintas.
En el ejemplo siguiente, una extensión de tipo opcional extiende el tipo System.Int32 con un método de extensión FromString que llama al miembro Parse estático. El método testFromString muestra que se llama al nuevo miembro exactamente igual que a cualquier miembro de instancia.
// Define a new member method FromString on the type Int32.
type System.Int32 with
member this.FromString( s : string ) =
System.Int32.Parse(s)
let testFromString str =
let mutable i = 0
// Use the extension method.
i <- i.FromString(str)
printfn "%d" i
testFromString "500"
El nuevo miembro de instancia aparecerá como cualquier otro método del tipo Int32 en IntelliSense, pero únicamente cuando el módulo que contiene la extensión está abierto o dentro del ámbito de algún otro modo.