Konstruktoren (F#)
In diesem Thema wird beschrieben, wie Konstruktoren definiert und verwendet werden, um Klassen- und Strukturobjekte zu erstellen und zu initialisieren.
Konstruktion von Klassenobjekten
Objekte von Klassentypen verfügen über Konstruktoren.Es gibt zwei Arten von Konstruktoren.Eine ist der primäre Konstruktor, dessen Parameter in Klammern direkt hinter dem Typnamen angezeigt werden.Mit dem new-Schlüsselwort geben Sie andere, optionale zusätzliche Konstruktoren an.Alle diese zusätzlichen Konstruktoren müssen den primären Konstruktor aufrufen.
Der primäre Konstruktor enthält let- und do-Bindungen, die am Anfang der Klassendefinition angezeigt werden.Mit der let-Bindung werden private Felder und Methoden der Klasse deklariert. Die do-Bindung führt Code aus.Weitere Informationen zu let-Bindungen in Klassenkonstruktoren finden Sie unter let-Bindungen in Klassen (F#).Weitere Informationen zu do-Bindungen in Konstruktoren finden Sie unter do-Bindungen in Klassen (F#).
Unabhängig davon, ob der Konstruktor, den Sie aufrufen möchten, ein primärer Konstruktor oder ein zusätzlicher Konstruktor ist, können Sie mit dem new-Ausdruck Objekte erstellen, und zwar mit oder ohne optionalem new-Schlüsselwort.Sie initialisieren die Objekte zusammen mit Konstruktorargumenten. Hierzu listen Sie die durch Kommas getrennten Argumente nacheinander auf und schließen Sie in Klammern ein, oder Sie verwenden benannte Argumente und Werte in Klammern.Sie können auch während der Konstruktion des Objekts Eigenschaften für ein Objekt festlegen, indem Sie wie bei der Verwendung benannter Konstruktorargumente Eigenschaftennamen Werte zuweisen.
Im folgenden Code wird eine Klasse veranschaulicht, die über einen Konstruktor und verschiedene Verfahren zum Erstellen von Objekten verfügt.
// 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()
Die Ausgabe lautet wie folgt.
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 von Strukturen
Für Strukturen gelten alle Regeln von Klassen.Daher können Sie über einen primären Konstruktor verfügen, und Sie können mit new zusätzliche Konstruktoren bereitstellen.Es gibt jedoch einen wichtigen Unterschied zwischen Strukturen und Klassen: Strukturen verfügen möglicherweise über einen Standardkonstruktor (d. h. ein Konstruktor ohne Argumente), auch wenn kein primärer Konstruktor definiert ist.Der Standardkonstruktor initialisiert alle Felder mit dem Standardwert für den Typ, normalerweise 0 (null) oder dem entsprechenden Wert.Alle Konstruktoren, die Sie für Strukturen definieren, müssen über mindestens ein Argument verfügen, damit kein Konflikt mit dem Standardkonstruktor verursacht wird.
Strukturen verfügen außerdem oft über Felder, die mit dem val-Schlüsselwort erstellt werden; Klassen können ebenfalls über diese Felder verfügen.Strukturen und Klassen, die mit dem val-Schlüsselwort definierte Felder aufweisen, können auch mit Datensatzausdrücken in zusätzlichen Konstruktoren initialisiert werden, wie im folgenden Code gezeigt.
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)
Weitere Informationen finden Sie unter Explizite Felder: Das val-Schlüsselwort (F#).
Ausführen von Nebeneffekten in Konstruktoren
Ein primärer Konstruktor in einer Klasse kann Code in einer do-Bindung ausführen.Wie gehen Sie jedoch vor, wenn Sie ohne do-Bindung Code in einem zusätzlichen Konstruktor ausführen müssen?Sie verwenden zu diesem Zweck das then-Schlüsselwort.
// 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()
Die Nebeneffekte des primären Konstruktors werden dennoch ausgeführt.Daher lautet die Ausgabe wie folgt.
Created a person object.
Created a person object.
Created an invalid person object.
Selbstbezeichner in Konstruktoren
In anderen Membern stellen Sie in der Definition jedes Members einen Namen für das aktuelle Objekt bereit.Sie können auch mit dem as-Schlüsselwort direkt hinter den Konstruktorparametern den Selbstbezeichner in die erste Zeile der Klassendefinition einfügen.Diese Syntax wird im folgenden Beispiel veranschaulicht.
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
In zusätzlichen Konstruktoren können Sie ebenfalls einen Selbstbezeichner definieren, indem Sie direkt hinter den Konstruktorparametern die as-Klausel einfügen.Diese Syntax wird im folgenden Beispiel veranschaulicht.
type MyClass2(x : int) =
member this.X = x
new() as this = MyClass2(0) then printfn "Initializing with X = %d" this.X
Wenn Sie versuchen, ein Objekt zu verwenden, bevor es vollständig definiert ist, können Probleme auftreten.Daher können durch die Verwendung des Selbstbezeichners vom Compiler eine Warnung ausgegeben und zusätzliche Überprüfungen eingefügt werden, um sicherzustellen, dass auf die Member eines Objekts nicht zugegriffen wird, bevor das Objekt initialisiert wird.Verwenden Sie den Selbstbezeichner nur in den do-Bindungen des primären Konstruktors oder in zusätzlichen Konstruktoren nach dem then-Schlüsselwort.
Der Name des Selbstbezeichners muss nicht this lauten.Es kann jeder gültige Bezeichner verwendet werden.
Zuweisen von Werten zu Eigenschaften bei der Initialisierung
Sie können im Initialisierungscode den Eigenschaften eines Klassenobjekts Werte zuweisen, indem Sie an die Argumentliste für einen Konstruktor eine Liste von Zuweisungen im Format property = value anhängen.Dies wird im folgenden Codebeispiel veranschaulicht.
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)
Die folgende Version des vorherigen Codes veranschaulicht die Kombination von üblichen Argumenten, optionalen Argumenten und Eigenschafteneinstellungen in einem einzigen Konstruktoraufruf.
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")
Statische Konstruktoren oder Typkonstruktoren
Zusätzlich zum Angeben von Code für die Erstellung von Objekten können statische let- und do-Bindungen in Klassentypen erstellt werden, die vor der ersten Verwendung des Typs für die Initialisierung auf Typebene ausgeführt werden.Weitere Informationen finden Sie unter let-Bindungen in Klassen (F#) und do-Bindungen in Klassen (F#).