Constructores
En este artículo se describe cómo definir y usar constructores para crear e inicializar objetos de clase y estructura.
Construcción de objetos de clase
Los objetos de tipos de clase tienen constructores. Existen dos tipos de constructores. Uno es el constructor principal, cuyos parámetros aparecen entre paréntesis justo después del nombre del tipo. Puede especificar otros constructores adicionales opcionales mediante la palabra clave new
. Cualquiera de estos constructores adicionales debe llamar al constructor principal.
El constructor principal contiene enlaces let
y do
que aparecen al principio de la definición de clase. Un enlace let
declara campos y métodos privados de la clase; un enlace do
ejecuta código. Para obtener más información sobre los let
enlaces en constructores de clases, vea let
Enlaces en clases. Para obtener más información sobre do
los enlaces en constructores, vea do
Enlaces en clases.
Independientemente de si el constructor al que desea llamar es un constructor principal o un constructor adicional, puede crear objetos mediante una new
expresión, con o sin la palabra clave opcional new
. Los objetos se inicializan junto con argumentos de constructor, ya sea enumerando los argumentos en orden y separados por comas y entre paréntesis, o mediante argumentos y valores con nombre entre paréntesis. También puedes establecer propiedades en un objeto durante la construcción del objeto mediante los nombres de propiedad y la asignación de valores igual que se usan argumentos de constructor con nombre.
En el código siguiente se muestra una clase que tiene un constructor y varias formas 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 como sigue:
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 las clases. Por lo tanto, puede tener un constructor principal y puede proporcionar constructores adicionales mediante new
. Sin embargo, hay una diferencia importante entre las estructuras y las clases: las estructuras pueden tener un constructor sin parámetros (es decir, uno sin argumentos) incluso si no se define ningún constructor principal. El constructor sin parámetros inicializa todos los campos en el valor predeterminado de ese tipo, normalmente cero o su equivalente. Los constructores que definas para las estructuras deben tener al menos un argumento para que no entren en conflicto con el constructor sin parámetros.
Además, las estructuras suelen tener campos creados mediante la val
palabra clave ; las clases también pueden tener estos campos. Las estructuras y clases que tienen campos definidos mediante la val
palabra clave también se pueden inicializar en constructores adicionales mediante expresiones de registro, 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, consulta Campos explícitos: la val
palabra clave.
Ejecución de efectos secundarios en constructores
Un constructor principal de una clase puede ejecutar código en un do
enlace. Sin embargo, ¿qué ocurre si tienes que ejecutar código en un constructor adicional, sin un do
enlace? Para ello, utiliza la palabra clavethen
.
// 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 principal se siguen ejecutando. Por tanto, la salida es la siguiente:
Created a person object.
Created a person object.
Created an invalid person object.
El motivo por el que then
es necesario en lugar de otro do
es que la do
palabra clave tiene su significado estándar de delimitar una unit
expresión que devuelve cuando está presente en el cuerpo de un constructor adicional. Solo tiene un significado especial en el contexto de los constructores principales.
Identificadores independientes en constructores
En otros miembros, se proporciona un nombre para el objeto actual en la definición de cada miembro. También puede colocar el identificador propio en la primera línea de la definición de clase mediante la as
palabra clave inmediatamente después de los parámetros del constructor. El siguiente ejemplo ilustra esta sintaxis.
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 constructores adicionales, también puede definir un identificador propio colocando la as
cláusula justo después de los parámetros del constructor. El siguiente ejemplo ilustra esta sintaxis:
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 usar un objeto antes de definirlo por completo. Por lo tanto, los usos del identificador propio pueden hacer que el compilador emita una advertencia e inserta comprobaciones adicionales para asegurarte de que no se tiene acceso a los miembros de un objeto antes de que se inicialice el objeto. Solo debes usar el identificador propio en los do
enlaces del constructor principal o después de la then
palabra clave en constructores adicionales.
El nombre del identificador propio no tiene que ser this
. Puede ser cualquier verificador válido.
Asignación de valores a propiedades en la inicialización
Puedes asignar valores a las propiedades de un objeto de clase en el código de inicialización anexando una lista de asignaciones del formulario 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)
La siguiente versión del código anterior muestra la combinación de argumentos normales, argumentos opcionales y valores de propiedad 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 de la clase heredada
Al heredar de una clase base que tiene un constructor, debes especificar sus argumentos en la cláusula heredada. Para obtener más información, vea Constructores y herencia.
Constructores estáticos o constructores de tipo
Además de especificar código para crear objetos, se pueden crear enlaces y let
estáticos do
en tipos de clase que se ejecutan antes de que el tipo se use primero para realizar la inicialización en el nivel de tipo. Para obtener más información, consulta let
Enlaces en clases y do
Enlaces en clases.