Osztályok (F#)
Az osztályok olyan típusok, amelyek tulajdonságokat, metódusokat és eseményeket tartalmazó objektumokat jelölnek.
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 ...
...
Megjegyzések
Az osztályok a .NET-objektumtípusok alapvető leírását képviselik; az osztály az F#-ban az objektumorientált programozást támogató elsődleges típuskoncepció.
Az előző szintaxisban az type-name
összes érvényes azonosító. Az type-params
opcionális általános típusparamétereket ismerteti. A típusparaméterek neveiből és szögletes zárójelek (<
és >
) közé zárt megkötésekből áll. További információ: Általános ésmegkötések. A parameter-list
konstruktor paramétereit ismerteti. Az első hozzáférési módosító a típusra vonatkozik; a második az elsődleges konstruktorra vonatkozik. Mindkét esetben az alapértelmezett érték.public
Az osztály alaposztályát a inherit
kulcsszó használatával adhatja meg. Az alaposztály-konstruktor argumentumait zárójelben kell megadnia.
Kötések használatával let
deklarálhatja az osztályhoz helyi mezőket vagy függvényértékeket, és be kell tartania a kötésekre vonatkozó let
általános szabályokat. A do-bindings
szakasz tartalmazza az objektumépítéskor végrehajtandó kódot.
További member-list
konstruktorokból, példány- és statikusmetódus-deklarációkból, felületi deklarációkból, absztrakt kötésekből, valamint tulajdonság- és eseménydeklarációkból áll. Ezeket a képviselők ismertetik.
Az identifier
opcionális as
kulcsszóval használt név a példány változójának vagy önazonosítójának ad nevet, amely a típusdefinícióban a típuspéldányra hivatkozik. További információt a jelen témakör Önazonosítók című szakaszában talál.
A definíció kezdetét és végét jelző kulcsszavak class
end
nem kötelezőek.
A kölcsönösen rekurzív típusok, amelyek egymásra hivatkoznak, ugyanúgy vannak összekapcsolva a and
kulcsszóval, mint a kölcsönösen rekurzív függvények. Például tekintse meg a kölcsönösen rekurzív típusok című szakaszt.
Konstruktorok
A konstruktor az osztálytípus egy példányát létrehozó kód. Az osztályok konstruktorai némileg másképp működnek F# nyelven, mint más .NET-nyelvekben. Az F#-osztályban mindig van egy elsődleges konstruktor, amelynek argumentumait a parameter-list
típusnevet követő szöveg írja le, és amelynek törzse az let
osztálydeklaráció elején lévő (és let rec
) kötésekből és az do
azt követő kötésekből áll. Az elsődleges konstruktor argumentumai hatókörben vannak az osztálydeklarációban.
A kulcsszóval new
további konstruktorokat is hozzáadhat tag hozzáadásához az alábbiak szerint:
new
(argument-list
) = constructor-body
Az új konstruktor törzsének meg kell hívnia az osztálydeklaráció tetején megadott elsődleges konstruktort.
Az alábbi példa ezt a fogalmat szemlélteti. Az alábbi kódban MyClass
két konstruktor található, egy elsődleges konstruktor, amely két argumentumot és egy másik konstruktort használ, amelyek nem vesznek fel argumentumokat.
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
Kötések használata és használata
Az let
osztálydefiníciók és do
kötések alkotják az elsődleges osztálykonstruktor törzsét, ezért az osztálypéldányok létrehozásakor futnak. Ha egy let
kötés függvény, akkor a függvény egy taggá lesz lefordítva. Ha a let
kötés egy függvényben vagy tagban nem használt érték, akkor a rendszer a konstruktor számára helyi változóvá állítja össze. Ellenkező esetben az osztály egy mezőjébe lesz lefordítva. Az do
alábbi kifejezések az elsődleges konstruktorba vannak lefordítva, és inicializálási kódot hajtanak végre minden példányhoz. Mivel minden további konstruktor mindig az elsődleges konstruktort hívja meg, a let
kötések és do
kötések mindig végrehajtásra kerülnek, függetlenül attól, hogy melyik konstruktort hívják meg.
A kötések által let
létrehozott mezők az osztály metódusai és tulajdonságai között érhetők el, azonban statikus metódusokból nem érhetők el, még akkor sem, ha a statikus metódusok paraméterként egy példányváltozót használnak. Ha létezik ilyen, az önazonosítóval nem érhetők el.
Önazonosítók
Az önazonosító az aktuális példányt jelképező név. Az önazonosítók a this
C# vagy a C++ Me
vagy a Visual Basic kulcsszóra hasonlítanak. Az önazonosítót kétféleképpen határozhatja meg attól függően, hogy az önazonosító a teljes osztálydefiníció hatókörébe tartozik-e, vagy csak egy adott metódusra.
Az egész osztály önazonosítójának meghatározásához használja a as
konstruktorparaméter-lista záró zárójelei utáni kulcsszót, és adja meg az azonosító nevét.
Ha csak egy metódushoz szeretne önazonosítót definiálni, adja meg az önazonosítót a tagdeklarációban, a metódus neve előtt, elválasztóként pedig egy pontot (.).
Az alábbi példakód bemutatja az önazonosítók létrehozásának két módját. Az első sorban a as
kulcsszó az önazonosító meghatározására szolgál. Az ötödik sorban az azonosító this
egy olyan önazonosító meghatározására szolgál, amelynek hatóköre a metódusra PrintMessage
korlátozódik.
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
Más .NET-nyelvektől eltérően tetszés szerint nevezheti el az önazonosítót; nem korlátozódik az olyan nevekre, mint a self
, Me
vagy this
.
A kulcsszóval deklarált önazonosító csak az as
alapkonstruktor után lesz inicializálva. Ezért az alapkonstruktor System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.
előtt vagy belsejében való használat esetén a futásidőben emeljük ki. Az önazonosítót az alapkonstruktor után szabadon használhatja, például kötésekben let
vagy do
kötésekben.
Általános típusparaméterek
Az általános típusparaméterek szögletes zárójelekben (<
és >
) vannak megadva egyetlen idézőjel és egy azonosító formájában. Több általános típusparamétert vessző választ el egymástól. Az általános típusparaméter hatókörben van a deklaráció során. Az alábbi kód példája az általános típusparaméterek megadását mutatja be.
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
A típus argumentumai a típus használatakor következtetnek. Az alábbi kódban a kikövetkeztetett típus a csuplok sorozata.
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
Öröklés megadása
A inherit
záradék azonosítja a közvetlen alaposztályt, ha van ilyen. Az F#-ban csak egy közvetlen alaposztály engedélyezett. Az osztály által implementálható felületek nem tekinthetők alaposztálynak. A felületeket az Interfészek témakör ismerteti.
Az alaposztály metódusait és tulajdonságait a származtatott osztályból úgy érheti el, hogy a nyelvi kulcsszót base
azonosítóként használja, majd egy pontot (.) és a tag nevét.
További információ: Öröklés.
Tagok szakasz
Ebben a szakaszban statikus vagy példánymódszereket, tulajdonságokat, felületi implementációkat, absztrakt tagokat, eseménydeklarációkat és további konstruktorokat határozhat meg. Nem jelenhetnek meg kötések ebben a szakaszban. Mivel a tagok az osztályok mellett számos F#-típushoz is hozzáadhatók, egy külön témakörben, a Tagokban tárgyaljuk őket.
Kölcsönösen rekurzív típusok
Ha körkörösen hivatkozó típusokat határoz meg, a kulcsszóval együtt sztringezi a and
típusdefiníciókat. A and
kulcsszó a kulcsszót az type
első definíció kivételével az alábbiak szerint cseréli le.
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
A kimenet az aktuális könyvtárban lévő összes fájl listája.
Mikor érdemes osztályokat, szakszervezeteket, rekordokat és struktúrákat használni?
A választható típusok sokfélesége miatt tisztában kell legyen azzal, hogy az egyes típusokat hogyan tervezték az adott helyzethez megfelelő típus kiválasztásához. Az osztályok objektumorientált programozási környezetekben való használatra lettek tervezve. Az objektumorientált programozás a .NET-keretrendszer írt alkalmazásokban használt domináns paradigma. Ha az F#-kódnak szorosan együtt kell működnie a .NET-keretrendszer vagy egy másik objektumorientált kódtárral, és különösen akkor, ha egy objektumorientált típusú rendszerből, például egy felhasználói felületi kódtárból kell kiterjeszteni, az osztályok valószínűleg megfelelőek.
Ha nem szorosan együttműködik az objektumorientált kódokkal, vagy ha önálló kódot ír, és így védve van az objektumorientált kódokkal való gyakori interakciótól, érdemes megfontolnia az osztályok, rekordok és diszkriminált egyesítések kombinációjának használatát. Az egyetlen, jól átgondolt diszkriminált egyesítés és a megfelelő mintamegfeleltetési kód gyakran használható az objektumhierarchiák egyszerűbb alternatívájaként. A diszkriminált szakszervezetekről további információt a diszkriminált szakszervezetek című témakörben talál.
A rekordok előnye, hogy egyszerűbbek, mint az osztályok, de a rekordok nem megfelelőek, ha egy típus követelményei túllépik az egyszerűségüket. A rekordok alapvetően egyszerű értékek aggregátumai, különálló konstruktorok nélkül, amelyek egyéni műveleteket hajthatnak végre rejtett mezők nélkül, öröklés vagy felületi implementációk nélkül. Bár a tagok, például a tulajdonságok és a metódusok hozzáadhatók a rekordokhoz a viselkedésük összetettebbé tétele érdekében, a rekordban tárolt mezők továbbra is az értékek egyszerű összesítése. A rekordokról további információt a Rekordok című témakörben talál.
A struktúrák kis adatösszesítésekhez is hasznosak, de különböznek az osztályoktól és a rekordoktól abban, hogy .NET-értéktípusok. Az osztályok és rekordok .NET-referenciatípusok. Az értéktípusok és referenciatípusok szemantikája abban különbözik, hogy az értéktípusokat érték adja át. Ez azt jelenti, hogy bitre másolják őket, amikor paraméterként adják át őket, vagy egy függvényből adják vissza őket. A rendszer a veremen is tárolja őket, vagy ha mezőként használják őket, a szülőobjektumba ágyazzák be őket ahelyett, hogy a halom saját különálló helyén lennének tárolva. Ezért a struktúrák megfelelőek a gyakran használt adatokhoz, ha a halom elérésének többletterhelése problémát jelent. További információ a struktúrákról: Structs.