Konstruktorer

Den här artikeln beskriver hur du definierar och använder konstruktorer för att skapa och initiera klass- och strukturobjekt.

Konstruktion av klassobjekt

Objekt av klasstyper har konstruktorer. Det finns två typer av konstruktorer. Den ena är den primära konstruktorn, vars parametrar visas inom parenteser strax efter typnamnet. Du anger andra valfria ytterligare konstruktorer med hjälp av nyckelordet new . Alla sådana ytterligare konstruktorer måste anropa den primära konstruktorn.

Den primära konstruktorn innehåller let och do bindningar som visas i början av klassdefinitionen. En let bindning deklarerar privata fält och metoder för klassen. En do bindning kör kod. Mer information om let bindningar i klasskonstruktorer finns let i Bindningar i klasser. Mer information om do bindningar i konstruktorer do finns i Bindningar i klasser.

Oavsett om konstruktorn som du vill anropa är en primär konstruktor eller en ytterligare konstruktor kan du skapa objekt med ett new uttryck, med eller utan det valfria new nyckelordet. Du initierar dina objekt tillsammans med konstruktorargument, antingen genom att visa argumenten i ordning och avgränsade med kommatecken och omges av parenteser, eller genom att använda namngivna argument och värden inom parenteser. Du kan också ange egenskaper för ett objekt under konstruktionen av objektet genom att använda egenskapsnamnen och tilldela värden precis som du använder namngivna konstruktorargument.

Följande kod illustrerar en klass som har en konstruktor och olika sätt att skapa objekt:

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

Utdata är följande:

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)

Konstruktion av strukturer

Strukturer följer alla klassers regler. Därför kan du ha en primär konstruktor och du kan tillhandahålla ytterligare konstruktorer med hjälp newav . Det finns dock en viktig skillnad mellan strukturer och klasser: strukturer kan ha en parameterlös konstruktor (dvs. en utan argument) även om ingen primär konstruktor har definierats. Den parameterlösa konstruktorn initierar alla fält till standardvärdet för den typen, vanligtvis noll eller motsvarande. Alla konstruktorer som du definierar för strukturer måste ha minst ett argument så att de inte står i konflikt med den parameterlösa konstruktorn.

Strukturer har också ofta fält som skapas med hjälp av nyckelordet val . Klasserna kan också ha dessa fält. Strukturer och klasser som har fält som definierats med hjälp av nyckelordet val kan också initieras i ytterligare konstruktorer med hjälp av postuttryck, som visas i följande kod.

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)

Mer information finns i Explicita fält: Nyckelordetval.

Köra biverkningar i konstruktorer

En primär konstruktor i en klass kan köra kod i en do bindning. Men vad händer om du måste köra kod i en ytterligare konstruktor, utan en do bindning? För att göra detta använder du nyckelordet 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()

Biverkningarna av den primära konstruktorn körs fortfarande. Därför är utdata följande:

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

Anledningen till varför then krävs i stället för en annan do är att nyckelordet do har sin standard innebörd att avgränsa ett unit-returning-uttryck när det finns i brödtexten i en ytterligare konstruktor. Den har bara särskild betydelse i samband med primära konstruktorer.

Självidentifierare i konstruktorer

I andra medlemmar anger du ett namn för det aktuella objektet i definitionen av varje medlem. Du kan också placera självidentifieraren på den första raden i klassdefinitionen med hjälp av nyckelordet as omedelbart efter konstruktorparametrarna. I följande exempel visas den här syntaxen.

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

I ytterligare konstruktorer kan du också definiera en självidentifierare genom att placera as satsen direkt efter konstruktorparametrarna. Följande exempel illustrerar den här syntaxen:

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

Problem kan uppstå när du försöker använda ett objekt innan det är helt definierat. Därför kan användning av självidentifieraren leda till att kompilatorn skickar en varning och infoga ytterligare kontroller för att säkerställa att medlemmarna i ett objekt inte nås innan objektet initieras. Du bör bara använda självidentifieraren i bindningarna för do den primära konstruktorn eller efter nyckelordet then i ytterligare konstruktorer.

Namnet på självidentifieraren behöver inte vara this. Det kan vara vilken giltig identifierare som helst.

Tilldela värden till egenskaper vid initiering

Du kan tilldela värden till egenskaperna för ett klassobjekt i initieringskoden genom att lägga till en lista över tilldelningar av formuläret property = value i argumentlistan för en konstruktor. Detta visas i följande kodexempel:

 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)

Följande version av den föregående koden illustrerar kombinationen av vanliga argument, valfria argument och egenskapsinställningar i ett konstruktoranrop:

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

Konstruktorer i ärvd klass

När du ärver från en basklass som har en konstruktor måste du ange dess argument i ärv-satsen. Mer information finns i Konstruktorer och arv.

Statiska konstruktorer eller typkonstruktorer

Förutom att ange kod för att skapa objekt kan statiska let och do bindningar redigeras i klasstyper som körs innan typen först används för att utföra initiering på typnivå. Mer information let finns i Bindningar i klasser och do bindningar i klasser.

Se även