Compartir a través de


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 valpalabra 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 unitexpresió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 doEnlaces en clases.

Consulte también