Metody
Metoda to funkcja skojarzona z typem. W programowaniu obiektowym metody są używane do uwidaczniania i implementowania funkcjonalności i zachowania obiektów i typów.
Składnia
// Instance method definition.
[ attributes ]
member [inline] self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Static method definition.
[ attributes ]
static member [inline] method-name parameter-list [ : return-type ] =
method-body
// Abstract method declaration or virtual dispatch slot.
[ attributes ]
abstract member method-name : type-signature
// Virtual method declaration and default implementation.
[ attributes ]
abstract member method-name : type-signature
[ attributes ]
default self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Override of inherited virtual method.
[ attributes ]
override self-identifier.method-name parameter-list [ : return-type ] =
method-body
// Optional and DefaultParameterValue attributes on input parameters
[ attributes ]
[ modifier ] member [inline] self-identifier.method-name ([<Optional; DefaultParameterValue( default-value )>] input) [ : return-type ]
Uwagi
W poprzedniej składni można zobaczyć różne formy deklaracji i definicji metod. W dłuższych ciałach metody podział wiersza jest zgodny ze znakiem równości (=), a cała treść metody jest wcięta.
Atrybuty można zastosować do dowolnej deklaracji metody. Poprzedzają składnię definicji metody i są zwykle wyświetlane w osobnym wierszu. Aby uzyskać więcej informacji, zobacz Atrybuty.
Metody można oznaczyć jako inline
. Aby uzyskać informacje o inline
systemie, zobacz Funkcje wbudowane.
Metody nieliniowe mogą być używane rekursywnie w obrębie typu; Nie ma potrzeby jawnego używania słowa kluczowego rec
.
Metody wystąpienia
Metody wystąpienia są deklarowane za pomocą słowa kluczowego member
i identyfikatora własnego, a następnie kropki (.) oraz nazwy metody i parametrów. Podobnie jak w przypadku let
powiązań, lista parametrów może być wzorcem. Zazwyczaj parametry metody są ujęte w nawiasy w postaci krotki, która jest sposobem, w jaki metody są wyświetlane w języku F# podczas ich tworzenia w innych językach programu .NET Framework. Jednak formularz curried (parametry oddzielone spacjami) jest również wspólny, a inne wzorce są również obsługiwane.
Poniższy przykład ilustruje definicję i użycie metody wystąpienia nie abstrakcyjnego.
type SomeType(factor0: int) =
let factor = factor0
member this.SomeMethod(a, b, c) = (a + b + c) * factor
member this.SomeOtherMethod(a, b, c) = this.SomeMethod(a, b, c) * factor
W ramach metod wystąpienia nie używaj identyfikatora samodzielnego do uzyskiwania dostępu do pól zdefiniowanych za pomocą powiązań let. Użyj identyfikatora samodzielnego podczas uzyskiwania dostępu do innych elementów członkowskich i właściwości.
Metody statyczne
Słowo kluczowe static
służy do określania, że można wywołać metodę bez wystąpienia i nie jest skojarzona z wystąpieniem obiektu. W przeciwnym razie metody są metodami wystąpień.
W przykładzie w następnej sekcji przedstawiono pola zadeklarowane za pomocą słowa kluczowego let
, składowe właściwości zadeklarowane za pomocą słowa kluczowego oraz metodę statyczną zadeklarowaną member
za pomocą słowa kluczowego static
.
Poniższy przykład ilustruje definicję i użycie metod statycznych. Załóżmy, że te definicje metod znajdują się w SomeType
klasie w poprzedniej sekcji.
static member SomeStaticMethod(a, b, c) =
(a + b + c)
static member SomeOtherStaticMethod(a, b, c) =
SomeType.SomeStaticMethod(a, b, c) * 100
Metody abstrakcyjne i wirtualne
Słowo kluczowe abstract
wskazuje, że metoda ma wirtualne miejsce wysyłania i może nie mieć definicji w klasie. Wirtualne miejsce wysyłania to wpis w wewnętrznie obsługiwanej tabeli funkcji używanych w czasie wykonywania do wyszukiwania wywołań funkcji wirtualnych w typie zorientowanym na obiekt. Mechanizm wysyłania wirtualnego to mechanizm, który implementuje polimorfizm, ważną cechę programowania obiektowego. Klasa, która ma co najmniej jedną metodę abstrakcyjną bez definicji, jest klasą abstrakcyjną, co oznacza, że nie można utworzyć wystąpień tej klasy. Aby uzyskać więcej informacji na temat klas abstrakcyjnych, zobacz Klasy abstrakcyjne.
Deklaracje metody abstrakcyjnej nie zawierają treści metody. Zamiast tego nazwa metody następuje dwukropek (:) i podpis typu dla metody . Sygnatura typu metody jest taka sama jak ta, która jest wyświetlana przez funkcję IntelliSense po wstrzymaniu wskaźnika myszy nad nazwą metody w Edytorze programu Visual Studio Code, z wyjątkiem bez nazw parametrów. Podpisy typów są również wyświetlane przez interpretera, fsi.exe, gdy pracujesz interaktywnie. Podpis typu metody jest tworzony przez wyświetlenie listy typów parametrów, po których następuje zwracany typ z odpowiednimi symbolami separatora. Parametry curried są oddzielone przez ->
parametry krotki i są oddzielone przez *
. Wartość zwracana jest zawsze oddzielona od argumentów symbolem ->
. Nawiasy mogą służyć do grupowania złożonych parametrów, takich jak wtedy, gdy typ funkcji jest parametrem, lub wskazać, kiedy krotka jest traktowana jako pojedynczy parametr, a nie jako dwa parametry.
Możesz również nadać domyślne definicje metod abstrakcyjnych, dodając definicję do klasy i używając słowa kluczowego default
, jak pokazano w bloku składni w tym temacie. Metoda abstrakcyjna, która ma definicję w tej samej klasie, jest równoważna metodzie wirtualnej w innych językach programu .NET Framework. Bez względu na to abstract
, czy definicja istnieje, słowo kluczowe tworzy nowe miejsce wysyłania w tabeli funkcji wirtualnej dla klasy.
Niezależnie od tego, czy klasa bazowa implementuje metody abstrakcyjne, klasy pochodne mogą zapewnić implementacje metod abstrakcyjnych. Aby zaimplementować metodę abstrakcyjną w klasie pochodnej, zdefiniuj metodę, która ma taką samą nazwę i podpis w klasie pochodnej, z wyjątkiem użycia override
słowa kluczowego lub default
i podaj treść metody. Słowa kluczowe override
i default
oznaczają dokładnie to samo. Użyj override
metody , jeśli nowa metoda zastępuje implementację klasy bazowej; użyj default
podczas tworzenia implementacji w tej samej klasie co oryginalna deklaracja abstrakcyjna. Nie należy używać słowa kluczowego abstract
w metodzie implementujące metodę, która została zadeklarowana jako abstrakcyjna w klasie bazowej.
Poniższy przykład ilustruje abstrakcyjną metodę Rotate
, która ma domyślną implementację, odpowiednik metody wirtualnej platformy .NET Framework.
type Ellipse(a0: float, b0: float, theta0: float) =
let mutable axis1 = a0
let mutable axis2 = b0
let mutable rotAngle = theta0
abstract member Rotate: float -> unit
default this.Rotate(delta: float) = rotAngle <- rotAngle + delta
Poniższy przykład ilustruje klasę pochodną, która zastępuje metodę klasy bazowej. W takim przypadku przesłonięcia zmieniają zachowanie, tak aby metoda nic nie zrobiła.
type Circle(radius: float) =
inherit Ellipse(radius, radius, 0.0)
// Circles are invariant to rotation, so do nothing.
override this.Rotate(_) = ()
Metody przeciążone
Metody przeciążone to metody, które mają identyczne nazwy w danym typie, ale mają różne argumenty. W języku F# argumenty opcjonalne są zwykle używane zamiast przeciążonych metod. Jednak metody przeciążone są dozwolone w języku, pod warunkiem, że argumenty są w postaci krotki, a nie w postaci curried.
Argumenty opcjonalne
Począwszy od języka F# 4.1, można również mieć opcjonalne argumenty z domyślną wartością parametru w metodach. Ułatwia to współdziałanie z kodem języka C#. W poniższym przykładzie przedstawiono składnię:
open System.Runtime.InteropServices
// A class with a method M, which takes in an optional integer argument.
type C() =
member _.M([<Optional; DefaultParameterValue(12)>] i) = i + 1
Należy pamiętać, że wartość przekazana dla elementu DefaultParameterValue
musi być zgodna z typem danych wejściowych. W powyższym przykładzie jest to int
. Próba przekazania wartości DefaultParameterValue
innej niż całkowita spowoduje błąd kompilacji.
Przykład: właściwości i metody
Poniższy przykład zawiera typ, który zawiera przykłady pól, funkcji prywatnych, właściwości i metody statycznej.
type RectangleXY(x1: float, y1: float, x2: float, y2: float) =
// Field definitions.
let height = y2 - y1
let width = x2 - x1
let area = height * width
// Private functions.
static let maxFloat (x: float) (y: float) = if x >= y then x else y
static let minFloat (x: float) (y: float) = if x <= y then x else y
// Properties.
// Here, "this" is used as the self identifier,
// but it can be any identifier.
member this.X1 = x1
member this.Y1 = y1
member this.X2 = x2
member this.Y2 = y2
// A static method.
static member intersection(rect1: RectangleXY, rect2: RectangleXY) =
let x1 = maxFloat rect1.X1 rect2.X1
let y1 = maxFloat rect1.Y1 rect2.Y1
let x2 = minFloat rect1.X2 rect2.X2
let y2 = minFloat rect1.Y2 rect2.Y2
let result: RectangleXY option =
if (x2 > x1 && y2 > y1) then
Some(RectangleXY(x1, y1, x2, y2))
else
None
result
// Test code.
let testIntersection =
let r1 = RectangleXY(10.0, 10.0, 20.0, 20.0)
let r2 = RectangleXY(15.0, 15.0, 25.0, 25.0)
let r3: RectangleXY option = RectangleXY.intersection (r1, r2)
match r3 with
| Some(r3) -> printfn "Intersection rectangle: %f %f %f %f" r3.X1 r3.Y1 r3.X2 r3.Y2
| None -> printfn "No intersection found."
testIntersection