Třídy (F#)
Třídy jsou typy, které představují objekty, které mohou mít vlastnosti, metody a události.
Syntaxe
// 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 ...
...
Poznámky
Třídy představují základní popis typů objektů .NET; třída je primární koncept typu, který podporuje objektově orientované programování v jazyce F#.
V předchozí syntaxi type-name
je libovolný platný identifikátor. Popisuje type-params
volitelné parametry obecného typu. Skládá se z názvů parametrů typu a omezení uzavřených v hranatých závorkách (<
a >
). Další informace naleznete v tématu Obecné typy a omezení. Popisuje parameter-list
parametry konstruktoru. První modifikátor přístupu se týká typu; druhý se týká primárního konstruktoru. V obou případech je public
výchozí hodnota .
Základní třídu třídy zadáte pomocí klíčového inherit
slova. Argumenty je nutné zadat v závorkách pro konstruktor základní třídy.
Deklarujete pole nebo hodnoty funkcí, které jsou pro třídu místní, pomocí let
vazeb a musíte dodržovat obecná pravidla pro let
vazby. Oddíl do-bindings
obsahuje kód, který se má provést při konstrukci objektu.
Skládá member-list
se z dalších konstruktorů, deklarací instance a statické metody, deklarací rozhraní, abstraktních vazeb a deklarací vlastností a událostí. Jsou popsány v části Členové.
Používá identifier
se s volitelným as
klíčovým slovem název proměnné instance nebo identifikátoru sebe, který lze použít v definici typu k odkazování na instanci typu. Další informace najdete v části Self Identifiers dále v tomto tématu.
Klíčová slova class
a end
označující začátek a konec definice jsou volitelná.
Vzájemně rekurzivní typy, což jsou typy, které vzájemně odkazují, jsou spojeny s klíčovým slovem and
stejně jako vzájemně rekurzivní funkce jsou. Příklad najdete v části Vzájemně se rekurzivní typy.
Konstruktory
Konstruktor je kód, který vytvoří instanci typu třídy. Konstruktory pro třídy fungují v jazyce F# trochu jinak než v jiných jazycích .NET. Ve třídě jazyka F# je vždy primární konstruktor, jehož argumenty jsou popsány za parameter-list
názvem typu a jehož tělo se skládá z let
vazeb (a let rec
) na začátku deklarace třídy a do
vazeb, které následují. Argumenty primárního konstruktoru jsou v oboru v rámci deklarace třídy.
Další konstruktory můžete přidat pomocí klíčového new
slova pro přidání člena následujícím způsobem:
new
(argument-list
) = constructor-body
Tělo nového konstruktoru musí vyvolat primární konstruktor zadaný v horní části deklarace třídy.
Následující příklad ukazuje tento koncept. V následujícím kódu MyClass
má dva konstruktory, primární konstruktor, který přebírá dva argumenty a jiný konstruktor, který nepřijímá žádné argumenty.
type MyClass1(x: int, y: int) =
do printfn "%d %d" x y
new() = MyClass1(0, 0)
let a do Bindings
do
Vazby let
v definici třídy tvoří tělo konstruktoru primární třídy, a proto se spouští při každém vytvoření instance třídy. Pokud je vazbou let
funkce, zkompiluje se do člena. let
Pokud je vazba hodnota, která se nepoužívá v žádné funkci nebo členu, je zkompilována do proměnné, která je místní pro konstruktor. V opačném případě se zkompiluje do pole třídy. Následující do
výrazy jsou zkompilovány do primárního konstruktoru a spouští inicializační kód pro každou instanci. Vzhledem k tomu, že všechny další konstruktory vždy volají primární konstruktor, let
vazby a do
vazby vždy spustí bez ohledu na to, který konstruktor je volána.
K polím vytvořeným vazbami let
lze přistupovat v rámci metod a vlastností třídy. Nelze k nim ale přistupovat ze statických metod, a to ani v případě, že statické metody jako parametr přebírají proměnnou instance. K nim nelze získat přístup pomocí identifikátoru sebe sama, pokud existuje.
Identifikátory sebe sama
Identifikátor sebe sama je název, který představuje aktuální instanci. Identifikátory sebe se podobají klíčovému slovu this
v jazyce C# nebo C++ nebo Me
v jazyce Visual Basic. Identifikátor sebeobsadu můžete definovat dvěma různými způsoby v závislosti na tom, jestli má být identifikátor sebeobsadu v oboru pro celou definici třídy, nebo jen pro jednotlivé metody.
Chcete-li definovat identifikátor sebe pro celou třídu, použijte as
klíčové slovo za závěrečnou závorkou seznamu parametrů konstruktoru a zadejte název identifikátoru.
Pokud chcete definovat identifikátor sebe sama pro jen jednu metodu, zadejte identifikátor sebe v deklaraci člena těsně před názvem metody a tečkou (.) jako oddělovač.
Následující příklad kódu znázorňuje dva způsoby vytvoření identifikátoru sebe sama. Na prvním řádku as
se klíčové slovo používá k definování identifikátoru sebe sama. V pátém řádku se identifikátor this
používá k definování vlastního identifikátoru, jehož obor je omezen na metodu PrintMessage
.
type MyClass2(dataIn) as self =
let data = dataIn
do
self.PrintMessage()
member this.PrintMessage() =
printf "Creating MyClass2 with Data %d" data
Na rozdíl od jiných jazyků rozhraní .NET můžete vlastní identifikátor pojmenovat, ale chcete; nejste omezeni na názvy, jako self
jsou , Me
nebo this
.
Identifikátor sebe, který je deklarován pomocí klíčového as
slova, není inicializován až po základní konstruktor. Proto při použití před nebo uvnitř základního konstruktoru bude System.InvalidOperationException: The initialization of an object or value resulted in an object or value being accessed recursively before it was fully initialized.
vyvolán během modulu runtime. Identifikátor sebe sama můžete použít volně za základním konstruktorem, například ve let
vazbách nebo do
vazbách.
Parametry obecného typu
Parametry obecného typu jsou zadány v hranatých závorkách (<
a >
), ve formě jednoduché uvozovky následované identifikátorem. Více parametrů obecného typu je odděleno čárkami. Parametr obecného typu je v oboru v celé deklaraci. Následující příklad kódu ukazuje, jak zadat parametry obecného typu.
type MyGenericClass<'a>(x: 'a) =
do printfn "%A" x
Argumenty typu se odvozují při použití typu. V následujícím kódu je odvozený typ posloupnosti řazených kolekcí členů.
let g1 = MyGenericClass(seq { for i in 1..10 -> (i, i * i) })
Určení dědičnosti
Klauzule inherit
identifikuje přímou základní třídu, pokud existuje. V jazyce F# je povolena pouze jedna přímá základní třída. Rozhraní, která implementuje třída, nejsou považována za základní třídy. Rozhraní jsou popsána v tématu Rozhraní .
K metodám a vlastnostem základní třídy můžete přistupovat z odvozené třídy pomocí klíčového slova base
jazyka jako identifikátoru, za kterým následuje tečka (.) a název člena.
Další informace najdete v tématu Dědičnost.
Oddíl Členové
V této části můžete definovat statické nebo instance metody, vlastnosti, implementace rozhraní, abstraktní členy, deklarace událostí a další konstruktory. V této části nelze zobrazit vazby a provádět vazby. Vzhledem k tomu, že kromě tříd je možné přidávat členy do různých typů jazyka F#, probírají se v samostatném tématu Členové.
Vzájemně se rekurzivní typy
Když definujete typy, které vzájemně odkazují cyklickým způsobem, zadáváte dohromady definice typů pomocí klíčového and
slova. Klíčové and
slovo nahradí type
klíčové slovo u všech kromě první definice následujícím způsobem.
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
Výstup je seznam všech souborů v aktuálním adresáři.
Kdy použít třídy, sjednocení, záznamy a struktury
Vzhledem k různým typům, ze kterých si můžete vybrat, musíte mít dobrou představu o tom, jaký typ je určený k výběru vhodného typu pro konkrétní situaci. Třídy jsou navržené pro použití v kontextech objektově orientovaného programování. Objektově orientované programování je dominantní paradigma používané v aplikacích, které jsou napsané pro rozhraní .NET Framework. Pokud váš kód jazyka F# musí úzce spolupracovat s rozhraním .NET Framework nebo jinou objektově orientované knihovnou, a zejména v případě, že je nutné rozšířit ze systému objektově orientovaného typu, jako je knihovna uživatelského rozhraní, jsou třídy pravděpodobně vhodné.
Pokud úzce nespolupracuje s objektově orientovaným kódem, nebo pokud píšete kód, který je samostatně obsažený, a proto je chráněn před častým interakcí s objektově orientovaným kódem, měli byste zvážit použití kombinace tříd, záznamů a diskriminovaných sjednocení. Jedinou, dobře promyšlenou diskriminovanou sjednocení spolu s odpovídajícím kódem vzorů lze často použít jako jednodušší alternativu k hierarchii objektů. Další informace o diskriminovaných svazech naleznete v tématu Diskriminované sjednocení.
Záznamy mají výhodu, že jsou jednodušší než třídy, ale záznamy nejsou vhodné, pokud požadavky typu překročí to, co je možné dosáhnout s jejich jednoduchostí. Záznamy jsou v podstatě jednoduché agregace hodnot bez samostatných konstruktorů, které mohou provádět vlastní akce bez skrytých polí a bez dědičnosti nebo implementace rozhraní. I když je možné do záznamů přidat členy, jako jsou vlastnosti a metody, aby jejich chování bylo složitější, pole uložená v záznamu jsou stále jednoduchou agregací hodnot. Další informace o záznamech naleznete v tématu Záznamy.
Struktury jsou také užitečné pro malé agregace dat, ale liší se od tříd a záznamů v tom, že jsou typy hodnot .NET. Třídy a záznamy jsou referenční typy .NET. Sémantika typů hodnot a odkazových typů se liší v tom, že typy hodnot se předávají podle hodnoty. To znamená, že se zkopírují bit pro bit, když jsou předány jako parametr nebo vráceny z funkce. Jsou také uloženy v zásobníku nebo, pokud se používají jako pole, vložené do nadřazeného objektu namísto uložení ve vlastním samostatném umístění v haldě. Proto jsou struktury vhodné pro často přístupná data, když je problém s režií při přístupu k haldě. Další informace o strukturách naleznete v tématu Struktury.