Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Klassen sind Typen, die Objekte darstellen, die Eigenschaften, Methoden und Ereignisse enthalten können.
Syntax
// Class definition:
type [access-modifier] type-name [type-params] [access-modifier] ( parameter-list ) [ as identifier ] =
[ class ]
[ inherit base-type-name(base-constructor-args) ]
[ let-bindings ]
[ do-bindings ]
member-list
...
[ end ]
// Mutually recursive class definitions:
type [access-modifier] type-name1 ...
and [access-modifier] type-name2 ...
...
Bemerkungen
Klassen stellen die grundlegende Beschreibung von .NET-Objekttypen dar; die Klasse ist das primäre Typkonzept, das die objektorientierte Programmierung in F# unterstützt.
In der vorherigen Syntax ist dies type-name ein beliebiger gültiger Bezeichner. In der type-params Beschreibung werden optionale generische Typparameter beschrieben. Sie besteht aus Typparameternamen und Einschränkungen, die in winkeln Klammern (< und >) eingeschlossen sind. Weitere Informationen finden Sie unter Generics und Constraints. In der parameter-list Beschreibung werden Konstruktorparameter beschrieben. Der erste Zugriffsmodifizierer bezieht sich auf den Typ; die zweite bezieht sich auf den primären Konstruktor. In beiden Fällen ist publicder Standardwert .
Sie geben die Basisklasse für eine Klasse mithilfe des Schlüsselworts inherit an. Sie müssen Argumente für den Basisklassenkonstruktor in Klammern angeben.
Sie deklarieren Felder oder Funktionswerte, die lokal für die Klasse sind, indem Sie Bindungen verwenden let , und Sie müssen die allgemeinen Regeln für let Bindungen befolgen. Der do-bindings Abschnitt enthält Code, der beim Erstellen von Objekten ausgeführt werden soll.
Das member-list besteht aus zusätzlichen Konstruktoren, Instanz- und statischen Methodendeklarationen, Schnittstellendeklarationen, abstrakten Bindungen und Eigenschaften- und Ereignisdeklarationen. Diese werden in "Member" beschrieben.
Das identifier mit dem optionalen as Schlüsselwort verwendete Schlüsselwort gibt der Instanzvariable oder dem Selbstbezeichner einen Namen, der in der Typdefinition verwendet werden kann, um auf die Instanz des Typs zu verweisen. Weitere Informationen finden Sie im Abschnitt "Self Identifiers" weiter unten in diesem Thema.
Die Schlüsselwörter class und end das Markieren des Anfangs- und Endes der Definition sind optional.
Sich gegenseitig rekursive Typen, bei denen es sich um Typen handelt, die miteinander verweisen, werden mit dem and Schlüsselwort verknüpft, genauso wie sich gegenseitig rekursive Funktionen befinden. Ein Beispiel finden Sie im Abschnitt "Sich gegenseitig rekursive Typen".
Erbauer
Der Konstruktor ist Code, der eine Instanz des Klassentyps erstellt. Konstruktoren für Klassen funktionieren in F# etwas anders als in anderen .NET-Sprachen. In einer F#-Klasse gibt es immer einen primären Konstruktor, dessen Argumente im parameter-list folgenden Typnamen beschrieben werden und dessen Textkörper aus den let (und let rec) Bindungen am Anfang der Klassendeklaration und den do folgenden Bindungen besteht. Die Argumente des primären Konstruktors befinden sich in der gesamten Klassendeklaration im Gültigkeitsbereich.
Sie können zusätzliche Konstruktoren hinzufügen, indem Sie das new Schlüsselwort verwenden, um ein Element wie folgt hinzuzufügen:
new(argument-list) = constructor-body
Der Textkörper des neuen Konstruktors muss den primären Konstruktor aufrufen, der oben in der Klassendeklaration angegeben ist.
Das folgende Beispiel veranschaulicht dieses Konzept. Im folgenden Code enthält zwei Konstruktoren, einen primären Konstruktor, MyClass der zwei Argumente und einen anderen Konstruktor verwendet, der keine Argumente akzeptiert.
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
Let and do Bindings
do Die let Bindungen in einer Klassendefinition bilden den Textkörper des primären Klassenkonstruktors und werden daher immer dann ausgeführt, wenn eine Klasseninstanz erstellt wird. Wenn es sich bei einer let Bindung um eine Funktion handelt, wird sie in ein Element kompiliert. Wenn es sich bei der let Bindung um einen Wert handelt, der in keiner Funktion oder einem Element verwendet wird, wird sie in eine Variable kompiliert, die lokal für den Konstruktor ist. Andernfalls wird sie in ein Feld der Klasse kompiliert. Die do folgenden Ausdrücke werden im primären Konstruktor kompiliert und Initialisierungscode für jede Instanz ausgeführt. Da alle zusätzlichen Konstruktoren immer den primären Konstruktor aufrufen, werden die let Bindungen und do Bindungen unabhängig davon, welcher Konstruktor aufgerufen wird, immer ausgeführt.
Auf Felder, die von let Bindungen erstellt werden, kann über die Methoden und Eigenschaften der Klasse zugegriffen werden. Auf sie kann jedoch nicht über statische Methoden zugegriffen werden, auch wenn die statischen Methoden eine Instanzvariable als Parameter verwenden. Auf sie kann nicht mithilfe des Selbstbezeichners zugegriffen werden, sofern vorhanden.
Selbstbezeichner
Ein Selbstbezeichner ist ein Name, der die aktuelle Instanz darstellt. Selbstbezeichner ähneln dem this Schlüsselwort in C# oder C++ oder Me Visual Basic. Sie können einen Selbstbezeichner auf zwei verschiedene Arten definieren, je nachdem, ob der Selbstbezeichner für die gesamte Klassendefinition oder nur für eine einzelne Methode im Gültigkeitsbereich sein soll.
Um einen Selbstbezeichner für die gesamte Klasse zu definieren, verwenden Sie das as Schlüsselwort nach den schließenden Klammern der Konstruktorparameterliste, und geben Sie den Bezeichnernamen an.
Um einen Selbstbezeichner für nur eine Methode zu definieren, geben Sie den Selbstbezeichner in der Memberdeklaration direkt vor dem Methodennamen und einem Punkt (.) als Trennzeichen an.
Im folgenden Codebeispiel werden die beiden Methoden zum Erstellen eines Selbstbezeichners veranschaulicht. In der ersten Zeile wird das as Schlüsselwort verwendet, um den Selbstbezeichner zu definieren. In der fünften Zeile wird der Bezeichner verwendet, um einen Selbstbezeichner this zu definieren, dessen Bereich auf die Methode PrintMessagebeschränkt ist.
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
Im Gegensatz zu anderen .NET-Sprachen können Sie den Selbstbezeichner wie gewünscht benennen. Sie sind nicht auf Namen wie self, , Meoder this.
Der selbstbezeichner, der mit dem as Schlüsselwort deklariert wird, wird erst nach dem Basiskonstruktor initialisiert. Daher wird, wenn sie vor oder innerhalb des Basiskonstruktors verwendet wird, System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. während der Laufzeit ausgelöst. Sie können den Selbstbezeichner frei nach dem Basiskonstruktor verwenden, z. B. in let Bindungen oder do Bindungen.
Generische Typparameter
Generische Typparameter werden in eckigen Klammern (< und >) in Form eines einzelnen Anführungszeichens gefolgt von einem Bezeichner angegeben. Mehrere generische Typparameter werden durch Kommas getrennt. Der generische Typparameter befindet sich in der gesamten Deklaration im Gültigkeitsbereich. Das folgende Codebeispiel zeigt, wie generische Typparameter angegeben werden.
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
Typargumente werden abgeleitet, wenn der Typ verwendet wird. Im folgenden Code ist der abgeleitete Typ eine Sequenz von Tupeln.
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
Angeben der Vererbung
Die inherit Klausel identifiziert die direkte Basisklasse, falls vorhanden. In F# ist nur eine direkte Basisklasse zulässig. Schnittstellen, die eine Klasse implementiert, gelten nicht als Basisklassen. Schnittstellen werden im Thema "Schnittstellen" erläutert.
Sie können auf die Methoden und Eigenschaften der Basisklasse aus der abgeleiteten Klasse zugreifen, indem Sie das Sprachschlüsselwort base als Bezeichner, gefolgt von einem Punkt (.) und dem Namen des Elements verwenden.
Weitere Informationen finden Sie unter Vererbung.
Abschnitt "Members"
Sie können statische Methoden oder Instanzmethoden, Eigenschaften, Schnittstellenimplementierungen, abstrakte Member, Ereignisdeklarationen und zusätzliche Konstruktoren in diesem Abschnitt definieren. Bindungen können in diesem Abschnitt nicht angezeigt werden. Da Mitglieder zusätzlich zu Klassen zu einer Vielzahl von F#-Typen hinzugefügt werden können, werden sie in einem separaten Thema behandelt, Member.
Sich gegenseitig rekursive Typen
Wenn Sie Typen definieren, die sich gegenseitig auf zirkuläre Weise referenzieren, zeichenfolgen Sie die Typdefinitionen mithilfe des and Schlüsselworts zusammen. Das and Schlüsselwort ersetzt das type Schlüsselwort für alle außer der ersten Definition wie folgt.
open System.IO
type Folder(pathIn: string) =
let path = pathIn
let filenameArray: string array = Directory.GetFiles(path)
member this.FileArray = Array.map (fun elem -> new File(elem, this)) filenameArray
and File(filename: string, containingFolder: Folder) =
member this.Name = filename
member this.ContainingFolder = containingFolder
let folder1 = new Folder(".")
for file in folder1.FileArray do
printfn "%s" file.Name
Die Ausgabe ist eine Liste aller Dateien im aktuellen Verzeichnis.
Verwendung von Klassen, Unions, Datensätzen und Strukturen
Angesichts der Vielzahl von Typen, aus denen Sie wählen können, müssen Sie ein gutes Verständnis dafür haben, was jeder Typ für die Auswahl des geeigneten Typs für eine bestimmte Situation vorgesehen ist. Klassen sind für die Verwendung in objektorientierten Programmierkontexten vorgesehen. Objektorientierte Programmierung ist das dominante Paradigma in Anwendungen, die für .NET Framework geschrieben wurden. Wenn Ihr F#-Code eng mit .NET Framework oder einer anderen objektorientierten Bibliothek zusammenarbeiten muss und insbesondere dann, wenn Sie von einem objektorientierten Typsystem wie einer UI-Bibliothek erweitern müssen, sind Klassen wahrscheinlich geeignet.
Wenn Sie nicht eng mit objektorientiertem Code interagieren oder Code schreiben, der eigenständig ist und daher vor häufigen Interaktionen mit objektorientiertem Code geschützt ist, sollten Sie eine Mischung aus Klassen, Datensätzen und diskriminierten Vereinigungen verwenden. Eine einzige, gut durchdachte diskriminierte Vereinigung, zusammen mit passendem Mustervergleichscode, kann häufig als einfachere Alternative zu einer Objekthierarchie verwendet werden. Weitere Informationen zu diskriminierten Gewerkschaften finden Sie unter Diskriminierte Gewerkschaften.
Datensätze haben den Vorteil, einfacher als Klassen zu sein, aber Datensätze sind nicht geeignet, wenn die Anforderungen eines Typs übersteigen, was mit ihrer Einfachheit erreicht werden kann. Datensätze sind im Grunde einfache Aggregate von Werten, ohne separate Konstruktoren, die benutzerdefinierte Aktionen ohne ausgeblendete Felder und ohne Vererbung oder Schnittstellenimplementierungen ausführen können. Obwohl Elemente wie Eigenschaften und Methoden Datensätzen hinzugefügt werden können, um ihr Verhalten komplexer zu gestalten, sind die in einem Datensatz gespeicherten Felder immer noch ein einfaches Aggregat von Werten. Weitere Informationen zu Datensätzen finden Sie unter "Datensätze".
Strukturen sind auch für kleine Aggregate von Daten nützlich, unterscheiden sich jedoch von Klassen und Datensätzen darin, dass sie .NET-Werttypen sind. Klassen und Datensätze sind .NET-Verweistypen. Die Semantik von Werttypen und Bezugstypen unterscheidet sich darin, dass Werttypen nach Wert übergeben werden. Dies bedeutet, dass sie bitweise kopiert werden, wenn sie als Parameter übergeben oder von einer Funktion zurückgegeben werden. Sie werden auch im Stapel gespeichert oder, wenn sie als Feld verwendet werden, in das übergeordnete Objekt eingebettet, anstatt an ihrer eigenen separaten Position auf dem Heap gespeichert. Daher sind Strukturen für häufig verwendete Daten geeignet, wenn der Aufwand für den Zugriff auf den Heap ein Problem darstellt. Weitere Informationen zu Strukturen finden Sie unter "Strukturen".