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 clases son tipos que representan objetos que pueden tener propiedades, métodos y eventos.
Sintaxis
// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
Observaciones
Las clases representan la descripción fundamental de los tipos de objeto de .NET; la clase es el concepto de tipo principal que admite la programación orientada a objetos en F#.
En la sintaxis anterior, type-name es cualquier identificador válido. Describe type-params los parámetros de tipo genérico opcionales. Consta de nombres de parámetros de tipo y restricciones entre corchetes angulares (< y >). Para obtener más información, vea Genéricos y restricciones. Describe parameter-list los parámetros del constructor. El primer modificador de acceso pertenece al tipo; el segundo pertenece al constructor principal. En ambos casos, el valor predeterminado es public.
Especifique la clase base para una clase mediante la inherit palabra clave . Debe proporcionar argumentos, entre paréntesis, para el constructor de clase base.
Declara campos o valores de función que son locales para la clase mediante let enlaces y debe seguir las reglas generales para let los enlaces. La do-bindings sección incluye código que se va a ejecutar tras la construcción de objetos.
member-list consta de constructores adicionales, declaraciones de métodos estáticos y de instancia, declaraciones de interfaz, enlaces abstractos y declaraciones de eventos y propiedades. Estos se describen en Miembros.
Que identifier se usa con la palabra clave opcional as proporciona un nombre a la variable de instancia o al identificador propio, que se puede usar en la definición de tipo para hacer referencia a la instancia del tipo. Para obtener más información, consulte la sección Identificadores independientes más adelante en este tema.
Las palabras clave class y end que marcan el inicio y el final de la definición son opcionales.
Los tipos mutuamente recursivos, que son tipos que se hacen referencia entre sí, se unen con la and palabra clave igual que las funciones recursivas mutuamente. Para obtener un ejemplo, vea la sección Tipos mutuamente recursivos.
Constructores
El constructor es código que crea una instancia del tipo de clase. Los constructores de las clases funcionan de forma algo diferente en F# que en otros lenguajes .NET. En una clase F#, siempre hay un constructor principal cuyos argumentos se describen en que parameter-list sigue el nombre de tipo y cuyo cuerpo consta de los let enlaces (y let rec) al principio de la declaración de clase y los do enlaces siguientes. Los argumentos del constructor principal están en el ámbito a lo largo de la declaración de clase.
Puede agregar constructores adicionales mediante la new palabra clave para agregar un miembro, como se indica a continuación:
new(argument-list) = constructor-body
El cuerpo del nuevo constructor debe invocar el constructor principal especificado en la parte superior de la declaración de clase.
En el ejemplo siguiente se muestra este concepto. En el código siguiente, MyClass tiene dos constructores, un constructor principal que toma dos argumentos y otro constructor que no toma ningún argumento.
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
let and do Bindings
Los let enlaces y do de una definición de clase forman el cuerpo del constructor de clase principal y, por tanto, se ejecutan cada vez que se crea una instancia de clase. Si un let enlace es una función, se compila en un miembro. Si el let enlace es un valor que no se usa en ninguna función o miembro, se compila en una variable que es local para el constructor. De lo contrario, se compila en un campo de la clase . Las do expresiones siguientes se compilan en el constructor principal y ejecutan el código de inicialización para cada instancia. Dado que los constructores adicionales siempre llaman al constructor principal, los let enlaces y do enlaces siempre se ejecutan independientemente del constructor al que se llame.
Se puede tener acceso a los campos creados por let enlaces a través de los métodos y propiedades de la clase; sin embargo, no se puede tener acceso a ellos desde métodos estáticos, incluso si los métodos estáticos toman una variable de instancia como parámetro. No se puede acceder a ellos mediante el identificador propio, si existe uno.
Identificadores independientes
Un identificador propio es un nombre que representa la instancia actual. Los identificadores independientes se asemejan a la this palabra clave en C# o C++ o Me en Visual Basic. Puede definir un identificador propio de dos maneras diferentes, en función de si desea que el identificador propio esté en el ámbito de toda la definición de clase o solo para un método individual.
Para definir un identificador propio para toda la clase, use la as palabra clave después de los paréntesis de cierre de la lista de parámetros del constructor y especifique el nombre del identificador.
Para definir un identificador propio para un solo método, proporcione el identificador propio en la declaración de miembro, justo antes del nombre del método y un punto (.) como separador.
En el ejemplo de código siguiente se muestran las dos maneras de crear un identificador propio. En la primera línea, la as palabra clave se usa para definir el identificador propio. En la quinta línea, el identificador this se usa para definir un identificador propio cuyo ámbito está restringido al método PrintMessage.
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
A diferencia de en otros lenguajes de .NET, puede asignar un nombre al identificador propio sin embargo; no está restringido a nombres como self, Meo this.
El identificador propio que se declara con la as palabra clave no se inicializa hasta después del constructor base. Por lo tanto, cuando se use antes o dentro del constructor base, System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. se generará durante el tiempo de ejecución. Puede usar libremente el identificador propio después del constructor base, como en let enlaces o do enlaces.
Parámetros de tipos genéricos
Los parámetros de tipo genérico se especifican entre corchetes angulares (< y >), en forma de comillas simples seguidas de un identificador. Varios parámetros de tipo genérico están separados por comas. El parámetro de tipo genérico está en el ámbito de toda la declaración. En el ejemplo de código siguiente se muestra cómo especificar parámetros de tipo genérico.
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
Los argumentos de tipo se deducen cuando se usa el tipo. En el código siguiente, el tipo inferido es una secuencia de tuplas.
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
Especificar herencia
La inherit cláusula identifica la clase base directa, si hay una. En F#, solo se permite una clase base directa. Las interfaces que implementa una clase no se consideran clases base. Las interfaces se describen en el tema Interfaces .
Puede acceder a los métodos y propiedades de la clase base desde la clase derivada mediante la palabra clave base language como identificador, seguido de un punto (.) y el nombre del miembro.
Para obtener más información, vea Herencia.
Sección Miembros
Puede definir métodos estáticos o de instancia, propiedades, implementaciones de interfaz, miembros abstractos, declaraciones de eventos y constructores adicionales en esta sección. Los enlaces let y do do no pueden aparecer en esta sección. Dado que los miembros se pueden agregar a una variedad de tipos de F# además de clases, se describen en un tema independiente, Miembros.
Tipos recursivos mutuamente
Cuando se definen tipos que se hacen referencia entre sí de forma circular, se encadena las definiciones de tipo mediante la and palabra clave . La and palabra clave reemplaza la type palabra clave en todas excepto la primera definición, como se indica a continuación.
open System.IO
type Folder(pathIn: string) =
let path = pathIn
let filenameArray: string array = Directory.GetFiles(path)
member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray
and File(filename: string, containingFolder: Folder) =
member this.Name = filename
member this.ContainingFolder = containingFolder
let folder1 = new Folder(".")
for file in folder1.FileArray do
printfn "%s" file.Name
La salida es una lista de todos los archivos del directorio actual.
Cuándo usar clases, uniones, registros y estructuras
Dada la variedad de tipos entre los que elegir, debe tener una buena comprensión de lo que cada tipo está diseñado para seleccionar el tipo adecuado para una situación determinada. Las clases están diseñadas para su uso en contextos de programación orientados a objetos. La programación orientada a objetos es el paradigma dominante que se usa en las aplicaciones escritas para .NET Framework. Si el código de F# tiene que trabajar estrechamente con .NET Framework u otra biblioteca orientada a objetos, y especialmente si tiene que extenderse desde un sistema de tipos orientado a objetos, como una biblioteca de interfaz de usuario, es probable que las clases sean adecuadas.
Si no interopera estrechamente con código orientado a objetos, o si está escribiendo código autocontenido y, por tanto, protegido frente a la interacción frecuente con código orientado a objetos, debe considerar la posibilidad de usar una combinación de clases, registros y uniones discriminadas. Una unión discriminada única y bien pensada, junto con el código de coincidencia de patrones adecuado, se puede usar a menudo como alternativa más sencilla a una jerarquía de objetos. Para obtener más información sobre las uniones discriminadas, vea Uniones discriminadas.
Los registros tienen la ventaja de ser más sencillos que las clases, pero los registros no son adecuados cuando las demandas de un tipo superan lo que se puede lograr con su simplicidad. Los registros son básicamente agregados simples de valores, sin constructores independientes que pueden realizar acciones personalizadas, sin campos ocultos y sin implementaciones de herencia o interfaz. Aunque se pueden agregar miembros como propiedades y métodos a registros para que su comportamiento sea más complejo, los campos almacenados en un registro siguen siendo un agregado simple de valores. Para obtener más información sobre los registros, vea Registros.
Las estructuras también son útiles para pequeños agregados de datos, pero difieren de las clases y registros en que son tipos de valor de .NET. Las clases y los registros son tipos de referencia de .NET. La semántica de los tipos de valor y los tipos de referencia son diferentes en que los tipos de valor se pasan por valor. Esto significa que se copian bits para bit cuando se pasan como un parámetro o se devuelven de una función. También se almacenan en la pila o, si se usan como campo, insertados dentro del objeto primario en lugar de almacenarse en su propia ubicación independiente en el montón. Por lo tanto, las estructuras son adecuadas para los datos a los que se accede con frecuencia cuando la sobrecarga de acceder al montón es un problema. Para obtener más información sobre las estructuras, vea Structs.