Costruttori

Questo articolo descrive come definire e usare costruttori per creare e inizializzare oggetti classe e struttura.

Costruzione di oggetti classe

Gli oggetti dei tipi di classe hanno costruttori. Esistono due tipi di costruttori. Uno è il costruttore primario, i cui parametri vengono visualizzati tra parentesi subito dopo il nome del tipo. È possibile specificare altri costruttori aggiuntivi facoltativi usando la new parola chiave . Qualsiasi costruttore aggiuntivo deve chiamare il costruttore primario.

Il costruttore primario contiene let associazioni e do visualizzate all'inizio della definizione della classe. Un'associazione let dichiara campi privati e metodi della classe. Un'associazione do esegue il codice. Per altre informazioni sulle let associazioni nei costruttori di classi, vedere let Binding in Classi. Per altre informazioni sulle do associazioni nei costruttori, vedere do Binding in Classi.

Indipendentemente dal fatto che il costruttore che si desidera chiamare sia un costruttore primario o un costruttore aggiuntivo, è possibile creare oggetti usando un'espressione new , con o senza la parola chiave facoltativa new . Inizializzare gli oggetti insieme agli argomenti del costruttore, elencando gli argomenti in ordine e separati da virgole e racchiusi tra parentesi oppure usando argomenti e valori denominati tra parentesi. È anche possibile impostare proprietà su un oggetto durante la costruzione dell'oggetto usando i nomi delle proprietà e assegnando valori esattamente come si usano argomenti del costruttore denominati.

Il codice seguente illustra una classe con un costruttore e vari modi di creare oggetti:

// 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()

L'output è il seguente:

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)

Costruzione di strutture

Le strutture seguono tutte le regole delle classi. Pertanto, è possibile avere un costruttore primario ed è possibile fornire costruttori aggiuntivi usando new. Tuttavia, esiste una differenza importante tra le strutture e le classi: le strutture possono avere un costruttore senza parametri ,ovvero uno senza argomenti, anche se non è definito alcun costruttore primario. Il costruttore senza parametri inizializza tutti i campi sul valore predefinito per quel tipo, in genere zero o il relativo equivalente. Tutti i costruttori definiti per le strutture devono avere almeno un argomento in modo che non siano in conflitto con il costruttore senza parametri.

Inoltre, le strutture includono spesso campi creati usando la val parola chiave . Anche le classi possono avere questi campi. Le strutture e le classi con campi definiti tramite la val parola chiave possono essere inizializzate anche in costruttori aggiuntivi usando espressioni di record, come illustrato nel codice seguente.

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)

Per altre informazioni, vedere Campi espliciti: parola val chiave.

Esecuzione di effetti collaterali nei costruttori

Un costruttore primario in una classe può eseguire codice in un'associazione do . Tuttavia, cosa accade se è necessario eseguire codice in un costruttore aggiuntivo, senza un'associazione do ? A tale scopo, usare la then parola chiave .

 // 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()

Gli effetti collaterali del costruttore primario continuano a essere eseguiti. Di conseguenza, l'output è il seguente:

Created a person object.
Created a person object.
Created an invalid person object.

Il motivo per cui then è necessario anziché un altro do è che la do parola chiave ha il significato standard di delimitare un'espressione che restituisce quando unitpresente nel corpo di un costruttore aggiuntivo. Ha un significato speciale solo nel contesto dei costruttori primari.

Identificatori self nei costruttori

In altri membri si specifica un nome per l'oggetto corrente nella definizione di ogni membro. È anche possibile inserire l'identificatore automatico nella prima riga della definizione della classe usando la as parola chiave immediatamente dopo i parametri del costruttore. Nell'esempio seguente viene illustrata questa sintassi.

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

Nei costruttori aggiuntivi è anche possibile definire un identificatore automatico inserendo la as clausola subito dopo i parametri del costruttore. L'esempio seguente illustra questa sintassi:

type MyClass2(x : int) =
    member this.X = x
    new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X

I problemi possono verificarsi quando si tenta di usare un oggetto prima che venga definito completamente. Pertanto, l'uso dell'identificatore automatico può causare l'emissione di un avviso da parte del compilatore e l'inserimento di controlli aggiuntivi per garantire che i membri di un oggetto non siano accessibili prima dell'inizializzazione dell'oggetto. È consigliabile usare l'identificatore automatico solo nelle do associazioni del costruttore primario o dopo la then parola chiave in costruttori aggiuntivi.

Il nome dell'identificatore self non deve essere this. Può essere qualsiasi identificatore valido.

Assegnazione di valori alle proprietà all'inizializzazione

È possibile assegnare valori alle proprietà di un oggetto classe nel codice di inizializzazione aggiungendo un elenco di assegnazioni del modulo property = value all'elenco di argomenti per un costruttore. Questo è illustrato nell'esempio di codice seguente:

 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 versione seguente del codice precedente illustra la combinazione di argomenti ordinari, argomenti facoltativi e impostazioni delle proprietà in una chiamata al costruttore:

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")

Costruttori nella classe ereditata

Quando eredita da una classe di base con un costruttore, è necessario specificare i relativi argomenti nella clausola inherit. Per altre informazioni, vedere Costruttori ed ereditarietà.

Costruttori statici o costruttori di tipo

Oltre a specificare il codice per la creazione di oggetti, è possibile creare associazioni e do statiche let nei tipi di classe eseguiti prima che il tipo venga usato per eseguire l'inizializzazione a livello di tipo. Per altre informazioni, vedere let Binding in classi e do associazioni nelle classi.

Vedi anche