Constructores (F#)
En este tema se describe cómo definir y utilizar los constructores para crear e inicializar objetos de clase y de estructura.
Construcción de objetos de clase
Los tipos de los objetos de clase tienen constructores. Hay dos tipos de constructores. Uno es el constructor primario, cuyos parámetros aparecen entre paréntesis justo después del nombre de tipo. Se pueden especificar otros constructores adicionales opcionales mediante la palabra clave new. Todos estos constructores adicionales deben llamar al constructor primario.
El constructor primario contiene los enlaces let y do que aparecen al principio de la definición de clase. Un enlace let declara los campos y métodos privados de la clase; un enlace do ejecuta código. Para obtener más información acerca de los enlaces let en los constructores de clase, vea Enlaces let en clases (F#). Para obtener más información acerca de los enlaces do en los constructores, vea Enlaces do en clases (F#).
Con independencia de si el constructor al que se desea llamar es primario o adicional, se pueden crear objetos mediante la expresión new, con o sin la palabra clave new opcional. Los objetos se inicializan junto con los argumentos de constructor, ya sea mediante una lista de los argumentos en orden, separados por comas y entre paréntesis, o bien mediante argumentos con nombre y valores entre paréntesis. También se pueden establecer las propiedades para un objeto durante la construcción de este último; para ello, se utilizan los nombres de propiedad y se asignan valores igual que se utilizan los argumentos de constructor con nombre.
En el código siguiente se muestra una clase que tiene un constructor y varias maneras de crear objetos.
// This class has a primary constructor that takes three arguments
// and an additional constructor that calls the primary constructor.
type MyClass(x0, y0, z0) =
let mutable x = x0
let mutable y = y0
let mutable z = z0
do
printfn "Initialized object that has coordinates (%d, %d, %d)" x y z
member this.X with get() = x and set(value) = x <- value
member this.Y with get() = y and set(value) = y <- value
member this.Z with get() = z and set(value) = z <- value
new() = MyClass(0, 0, 0)
// Create by using the new keyword.
let myObject1 = new MyClass(1, 2, 3)
// Create without using the new keyword.
let myObject2 = MyClass(4, 5, 6)
// Create by using named arguments.
let myObject3 = MyClass(x0 = 7, y0 = 8, z0 = 9)
// Create by using the additional constructor.
let myObject4 = MyClass()
La salida es la siguiente.
Initialized object that has coordinates (1, 2, 3)
Initialized object that has coordinates (4, 5, 6)
Initialized object that has coordinates (7, 8, 9)
Initialized object that has coordinates (0, 0, 0)
Construcción de estructuras
Las estructuras siguen todas las reglas de clases. Por consiguiente, puede haber un constructor primario y se pueden proporcionar constructores adicionales mediante new. Sin embargo, existe una diferencia importante entre las estructuras y las clases: las primeras pueden tener un constructor predeterminado (es decir, que no tiene argumentos) aunque no se defina ningún constructor primario. El constructor predeterminado inicializa todos los campos en su valor predeterminado para el tipo de que se trate, que suele ser cero o su equivalente. Todos los constructores que se definen para estructuras deben tener como mínimo un argumento para que no estén en conflicto con el constructor predeterminado.
Asimismo, a menudo las estructuras tienen campos que se crean mediante la palabra clave val; las clases también pueden tener estos campos. Las estructuras y las clases que tienen campos definidos mediante la palabra clave val también se pueden inicializar en constructores adicionales mediante expresiones de registro, tal y como se muestra en el código siguiente.
type MyStruct =
struct
val X : int
val Y : int
val Z : int
new(x, y, z) = { X = x; Y = y; Z = z }
end
let myStructure1 = new MyStruct(1, 2, 3)
Para obtener más información, vea Campos explícitos: palabra clave val (F#).
Ejecutar efectos secundarios en constructores
Un constructor primario de una clase puede ejecutar código en un enlace do. Sin embargo, ¿qué sucede si hay que ejecutar código en un constructor adicional, sin un enlace do? Para ello, se utiliza la palabra clave then.
// Executing side effects in the primary constructor and
// additional constructors.
type Person(nameIn : string, idIn : int) =
let mutable name = nameIn
let mutable id = idIn
do printfn "Created a person object."
member this.Name with get() = name and set(v) = name <- v
member this.ID with get() = id and set(v) = id <- v
new() =
Person("Invalid Name", -1)
then
printfn "Created an invalid person object."
let person1 = new Person("Humberto Acevedo", 123458734)
let person2 = new Person()
Los efectos secundarios del constructor primario se ejecutan de todas formas. Así pues, la salida es la siguiente.
Created a person object.
Created a person object.
Created an invalid person object.
Autoidentificadores en constructores
En otros miembros, se proporciona un nombre para el objeto actual en la definición de cada miembro. También se puede colocar el autoidentificador en la primera línea de la definición de clase mediante la palabra clave as que aparece inmediatamente después de los parámetros de constructor. Esta sintaxis se muestra en el siguiente ejemplo:
type MyClass1(x) as this =
// This use of the self identifier produces a warning - avoid.
let x1 = this.X
// This use of the self identifier is acceptable.
do printfn "Initializing object with X =%d" this.X
member this.X = x
En los constructores adicionales, también se puede definir un autoidentificador colocando la cláusula as justo después de los parámetros de constructor. Esta sintaxis se muestra en el siguiente ejemplo:
type MyClass2(x : int) =
member this.X = x
new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X
Pueden producirse problemas al intentar utilizar un objeto antes de definirlo totalmente. Por ello, cualquier uso del autoidentificador puede dar lugar a que el compilador emita una advertencia e inserte comprobaciones adicionales para asegurarse de que no se obtenga acceso a los miembros de un objeto antes de que este se inicialice. El autoidentificador únicamente debe utilizarse en los enlaces do del constructor primario o después de la palabra clave then en los constructores adicionales.
El nombre del autoidentificador no tiene que ser this. Puede ser cualquier identificador válido.
Asignar valores a propiedades en la inicialización
Se pueden asignar valores a las propiedades de un objeto de clase en el código de inicialización; para ello, se anexa una lista de asignaciones con la forma property = value a la lista de argumentos de un constructor. Esto se muestra en el ejemplo de código siguiente.
type Account() =
let mutable balance = 0.0
let mutable number = 0
let mutable firstName = ""
let mutable lastName = ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(AccountNumber=8782108,
FirstName="Darren", LastName="Parker",
Balance=1543.33)
En la versión siguiente del código anterior se muestra la combinación de argumentos ordinarios, argumentos opcionales y valores de propiedades en una llamada de constructor.
type Account(accountNumber : int, ?first: string, ?last: string, ?bal : float) =
let mutable balance = defaultArg bal 0.0
let mutable number = accountNumber
let mutable firstName = defaultArg first ""
let mutable lastName = defaultArg last ""
member this.AccountNumber
with get() = number
and set(value) = number <- value
member this.FirstName
with get() = firstName
and set(value) = firstName <- value
member this.LastName
with get() = lastName
and set(value) = lastName <- value
member this.Balance
with get() = balance
and set(value) = balance <- value
member this.Deposit(amount: float) = this.Balance <- this.Balance + amount
member this.Withdraw(amount: float) = this.Balance <- this.Balance - amount
let account1 = new Account(8782108, bal = 543.33,
FirstName="Raman", LastName="Iyer")
Constructores estáticos o constructores de tipo
Además de especificar código para crear objetos, los enlaces let y do estáticos se pueden crear en tipos de clase que se ejecutan antes de que el tipo se utilice por primera vez para realizar la inicialización en el nivel de tipo. Para obtener más información, vea Enlaces let en clases (F#) y Enlaces do en clases (F#).
Vea también
Conceptos
Historial de cambios
Fecha |
Historial |
Motivo |
---|---|---|
Mayo de 2010 |
Se ha corregido un problema en el último ejemplo de código. |
Corrección de errores de contenido. |