Dela via


Klasser (F#)

Klasser är typer som representerar objekt som kan ha egenskaper, metoder och händelser.

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 ...
...

Kommentarer

Klasser representerar den grundläggande beskrivningen av .NET-objekttyper. klassen är det primära typkonceptet som stöder objektorienterad programmering i F#.

I föregående syntax type-name är en giltig identifierare. Beskriver type-params valfria generiska typparametrar. Den består av typparameternamn och begränsningar som omges av vinkelparenteser (< och >). Mer information finns i Generiska objekt och begränsningar. Beskriver parameter-list konstruktorparametrar. Den första åtkomstmodifieraren avser typen; den andra gäller den primära konstruktorn. I båda fallen är publicstandardvärdet .

Du anger basklassen för en klass med hjälp av nyckelordet inherit . Du måste ange argument i parenteser för basklasskonstruktorn.

Du deklarerar fält eller funktionsvärden som är lokala för klassen med hjälp let av bindningar, och du måste följa de allmänna reglerna för let bindningar. Avsnittet do-bindings innehåller kod som ska köras vid objektkonstruktion.

Består member-list av ytterligare konstruktorer, instans- och statiska metoddeklarationer, gränssnittsdeklarationer, abstrakta bindningar samt egenskaps- och händelsedeklarationer. Dessa beskrivs i Ledamöterna.

Det identifier som används med det valfria as nyckelordet ger ett namn till instansvariabeln, eller självidentifierare, som kan användas i typdefinitionen för att referera till instansen av typen. Mer information finns i avsnittet Självidentifierare senare i det här avsnittet.

class Nyckelorden och end som markerar början och slutet av definitionen är valfria.

Ömsesidigt rekursiva typer, som är typer som refererar till varandra, kopplas ihop med nyckelordet and precis som ömsesidigt rekursiva funktioner är. Ett exempel finns i avsnittet Ömsesidigt rekursiva typer.

Konstruktorer

Konstruktorn är kod som skapar en instans av klasstypen. Konstruktorer för klasser fungerar något annorlunda i F# än på andra .NET-språk. I en F#-klass finns det alltid en primär konstruktor vars argument beskrivs i parameter-list som följer typnamnet och vars brödtext består av let (och let rec) bindningar i början av klassdeklarationen och bindningarna do som följer. Argumenten för den primära konstruktorn finns i omfånget i hela klassdeklarationen.

Du kan lägga till ytterligare konstruktorer med hjälp av nyckelordet new för att lägga till en medlem på följande sätt:

new(argument-list) = constructor-body

Den nya konstruktorns brödtext måste anropa den primära konstruktorn som anges högst upp i klassdeklarationen.

I följande exempel visas det här konceptet. I följande kod MyClass finns två konstruktorer, en primär konstruktor som tar två argument och en annan konstruktor som inte tar några argument.

type MyClass1(x: int, y: int) =
    do printfn "%d %d" x y
    new() = MyClass1(0, 0)

let and do Bindings

Bindningarna let och do i en klassdefinition utgör brödtexten för den primära klasskonstruktorn, och därför körs de när en klassinstans skapas. Om en let bindning är en funktion kompileras den till en medlem. Om bindningen let är ett värde som inte används i någon funktion eller medlem kompileras den till en variabel som är lokal för konstruktorn. Annars kompileras den till ett fält i klassen. De do uttryck som följer kompileras till den primära konstruktorn och kör initieringskoden för varje instans. Eftersom alla ytterligare konstruktorer alltid anropar den primära konstruktorn körs let bindningarna och do bindningarna alltid oavsett vilken konstruktor som anropas.

Fält som skapas av let bindningar kan nås i klassens metoder och egenskaper. De kan dock inte nås från statiska metoder, även om de statiska metoderna använder en instansvariabel som en parameter. De kan inte nås med hjälp av självidentifieraren, om det finns en sådan.

Självidentifierare

En självidentifierare är ett namn som representerar den aktuella instansen. Självidentifierare liknar nyckelordet this i C# eller C++ eller Me i Visual Basic. Du kan definiera en självidentifierare på två olika sätt, beroende på om du vill att självidentifieraren ska finnas i omfånget för hela klassdefinitionen eller bara för en enskild metod.

Om du vill definiera en självidentifierare för hela klassen använder du nyckelordet as efter de avslutande parenteserna i konstruktorparameterlistan och anger identifierarnamnet.

Om du vill definiera en självidentifierare för bara en metod anger du självidentifieraren i medlemsdeklarationen, precis före metodnamnet och en punkt (.) som avgränsare.

I följande kodexempel visas de två sätten att skapa en självidentifierare. På den första raden används nyckelordet as för att definiera självidentifieraren. På den femte raden används identifieraren this för att definiera en självidentifierare vars omfång är begränsat till metoden PrintMessage.

type MyClass2(dataIn) as self =
    let data = dataIn
    do
        self.PrintMessage()
    member this.PrintMessage() =
        printf "Creating MyClass2 with Data %d" data

Till skillnad från i andra .NET-språk kan du namnge självidentifieraren hur du vill. du är inte begränsad till namn som self, Meeller this.

Självidentifieraren som deklareras med nyckelordet as initieras inte förrän efter baskonstruktorn. När det används före eller inuti baskonstruktorn System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized. utlöses därför under körningen. Du kan använda självidentifieraren fritt efter baskonstruktorn, till exempel i let bindningar eller do bindningar.

Allmänna typparametrar

Generiska typparametrar anges i vinkelparenteser (< och >), i form av ett enkelt citattecken följt av en identifierare. Flera generiska typparametrar avgränsas med kommatecken. Den generiska typparametern finns i omfånget i hela deklarationen. I följande kodexempel visas hur du anger parametrar av allmän typ.

type MyGenericClass<'a>(x: 'a) =
    do printfn "%A" x

Typargument härleds när typen används. I följande kod är den härledda typen en sekvens med tupplar.

let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })

Ange arv

Satsen inherit identifierar den direkta basklassen, om det finns en. I F# tillåts endast en direkt basklass. Gränssnitt som en klass implementerar betraktas inte som basklasser. Gränssnitt beskrivs i avsnittet Gränssnitt .

Du kan komma åt metoderna och egenskaperna för basklassen från den härledda klassen med hjälp av språknyckelordet base som identifierare, följt av en punkt (.) och namnet på medlemmen.

Mer information finns i Arv.

Avsnittet Medlemmar

Du kan definiera statiska metoder eller instansmetoder, egenskaper, gränssnittsimplementeringar, abstrakta medlemmar, händelsedeklarationer och ytterligare konstruktorer i det här avsnittet. Det går inte att visa bindningar i det här avsnittet. Eftersom medlemmar kan läggas till i en mängd olika F#-typer utöver klasser, diskuteras de i ett separat ämne, Medlemmar.

Ömsesidigt rekursiva typer

När du definierar typer som refererar till varandra på ett cirkulärt sätt, stränger du samman typdefinitionerna med hjälp av nyckelordet and . Nyckelordet and ersätter nyckelordet type för alla utom den första definitionen enligt följande.

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

Utdata är en lista över alla filer i den aktuella katalogen.

När du ska använda klasser, fackföreningar, poster och strukturer

Med tanke på de olika typerna att välja mellan måste du ha en god förståelse för vad varje typ är utformad för för att välja lämplig typ för en viss situation. Klasser är utformade för användning i objektorienterade programmeringskontexter. Objektorienterad programmering är det dominerande paradigmet som används i program som är skrivna för .NET Framework. Om F#-koden måste ha ett nära samarbete med .NET Framework eller ett annat objektorienterat bibliotek, och särskilt om du måste utöka från ett objektorienterat typsystem, till exempel ett gränssnittsbibliotek, är klasser förmodligen lämpliga.

Om du inte samverkar nära med objektorienterad kod, eller om du skriver kod som är fristående och därför skyddas från frekvent interaktion med objektorienterad kod, bör du överväga att använda en blandning av klasser, poster och diskriminerade fackföreningar. En enda, väl genomtänkt diskrimineringsunion, tillsammans med lämplig mönstermatchningskod, kan ofta användas som ett enklare alternativ till en objekthierarki. Mer information om diskriminerade fackföreningar finns i Diskriminerade fackföreningar.

Poster har fördelen att vara enklare än klasser, men poster är inte lämpliga när kraven av en typ överskrider vad som kan åstadkommas med deras enkelhet. Poster är i princip enkla aggregeringar av värden, utan separata konstruktorer som kan utföra anpassade åtgärder, utan dolda fält och utan arvs- eller gränssnittsimplementeringar. Även om medlemmar som egenskaper och metoder kan läggas till i poster för att göra deras beteende mer komplext, är fälten som lagras i en post fortfarande en enkel mängd värden. Mer information om poster finns i Poster.

Strukturer är också användbara för små mängder data, men de skiljer sig från klasser och poster eftersom de är .NET-värdetyper. Klasser och poster är .NET-referenstyper. Semantiken för värdetyper och referenstyper skiljer sig i att värdetyper skickas efter värde. Det innebär att de kopieras bit för bit när de skickas som en parameter eller returneras från en funktion. De lagras också i stacken eller, om de används som ett fält, inbäddade i det överordnade objektet i stället för att lagras på en egen separat plats i heapen. Därför är strukturer lämpliga för data som används ofta när kostnaderna för att komma åt heapen är ett problem. Mer information om strukturer finns i Structs.

Se även