Operacja (Operation)
W tej sekcji użytkownik może tworzyć kolejne elementy klasy w języku programowania F#. Są to między innymi właściwości (ang. Properties) oraz metody (ang. Methods). Jest to przedstawione na rysunku 1 poniżej.
Elementy te różnią się od atrybutów dostępem. Atrybuty są prywatne, natomiast operacje mogą mieć różne formy dostępu. Dodatkowo operacje nie są wywoływane podczas konstruowania typu, w przeciwieństwie do atrybutów.
W ramach opracowanej wtyczki udało się zrealizować funkcjonalność, dzięki której w sekcji tej można tworzyć dwa rodzaje elementów:
- Właściwości (ang. Properties)
- Metody (ang. Methods)
Elementy te należy traktować jako części składowe klasy/typu w języku programowania F#. Poniżej znajdują się szczegółowe opisy tych dwóch konstrukcji.
Właściwość (Property)
Właściwościami nazywamy takie elementy, które wartościami są związane z obiektem.
Właściwość deklaruje się poprzez użycie słowa kluczowego member, wartość identyfikatora samego siebie(Identifier), który reprezentuje bieżącą instancję obiektu. Domyślnie w projekcie jest to nazwa this, lecz może ona być zmieniona na dowolnie inną we właściwości Identifier nadrzędnej klasy. We właściwościach definiuje się operacje get i set, które nazywa się akcesorami (ang. accessors). Możliwe są różne warianty do zdefiniowania właściwości, są one przedstawione na listingu 1. Jeśli chcemy definiować właściwość tylko do odczytu, można zdefiniować tylko operację get. Natomiast jeśli chcemy stworzyć właściwość tylko do zapisu, należy użyć tylko operacji set. Można definiować obie operacje jednocześnie. Zostało to zaimplementowane w zaproponowanym rozwiązaniu.
// Property that has both get and set defined. [ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName with [accessibility-modifier] get() = get-function-body and [accessibility-modifier] set parameter = set-function-body // Alternative syntax for a property that has get and set. [ attributes-for-get ] [ static ] member [accessibility-modifier-for-get] [self-identifier.]PropertyName = get-function-body [ attributes-for-set ] [ static ] member [accessibility-modifier-for-set] [self-identifier.]PropertyName with set parameter = set-function-body // Property that has get only. [ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName = get-function-body // Alternative syntax for property that has get only. [ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName with get() = get-function-body // Property that has set only. [ attributes ] [ static ] member [accessibility-modifier] [self-identifier.]PropertyName with set parameter = set-function-body
Listing 1: Definicja właściwości w klasie/typie
Jak widać na listingu 1, właściwości można definiować na kilka różnych sposobów. W pluginie zostały wykorzystane dwa ostatnie sposoby do definiowania właściwości set oraz get, a także pierwszy sposób w przypadku gdy użytkownik chce zdefiniować oba akcesory jednocześnie.
Domyślnie właściwości są publiczne (ang. public). Modyfikatory dostępu także mogą być użyte w przypadku właściwości. Jeśli ustawimy modyfikator dostępu bezpośrednio przed nazwą właściwości, to odnosi się on do obu akcesorów jednocześnie. Jeśli jednak potrzebujemy różnej dostępności akcesorów, możemy to zrobić, ustawiając poziom dostępu dla każdego akcesora oddzielnie. Robi się to poprzez dodanie modyfikatora bezpośrednio przed nazwą set lub get.
W modelu możemy użyć jednego modyfikatora dostępu dla obu akcesorów jednocześnie. Jeśli jednak chcemy, aby było inaczej, należy wygenerowany fragment kodu źródłowego dostosować do swoich potrzeb.
Przykład użycia przedstawiony jest na listingu 2 poniżej.
// A read-only property. member this.MyReadOnlyProperty = myInternalValue // A write-only property. member this.MyWriteOnlyProperty with set (value) = myInternalValue <- value // A read-write property. member this.MyReadWriteProperty with get () = myInternalValue and set (value) = myInternalValue <- value
Listing 2: Przykład użycia właściwości
We właściwościach możemy jawnie podawać typy zwracane przez akcesor get. Jest to przedstawione na poniższym przykładzie 3.
member this.MyProperty2 with get() : int = myInternalValue
Listing 2: Przykład użycia akcesora get z podanym zwracanym typem
Metoda (Method)
Metoda jest funkcją, która jest powiązana z klasą/typem. Są one stosowane w celu wdrożenia pewnej funkcjonalności i zachowania obiektów.
Na listingu 3 widać składnię definiującą metody w klasie/typie. Są tu widoczne różne formy deklaracji metod. Na ogół ciało metody umieszcza się w nowej linii po znaku równości z wcięciem.
// 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 self-identifier.method-name : type-signature // Virtual method declaration and default implementation. [ attributes ] abstract member self-identifier.method-name : type-signature [ attributes ] default member self-identifier.method-name parameter-list[ : return-type ] = method-body // Override of inherited virtual method. [ attributes ] override member self-identifier.method-name parameter-list [ : return-type ]= method-body
Listing 3: Definicja metod w klasie/typie
Atrybuty w takiej formie – [odwołanie] – umieszcza się przed deklarację metody. Jest to rzadko spotykane. Jeśli jednak użytkownik, modelując jakiś program, stwierdzi, że musi zadeklarować właśnie taki atrybut zgodny z platformą .NET, to jest to możliwe (właściwość Operation Attributes). Bardziej szczegółowy opis znajduje się w poście z przykładami użycia operacji.
Metody oznaczone jako inline nie są zmieniane przez optymalizator kodu podczas kompilowania. Ich ciało jest wstawiane w miejscu wywołania.
Na listingu 4 poniżej widać przykładową metodę sumującą podane trzy argumenty.
member this.AddNumbers a b c = a + b + c
Listing 4: Przykład użycia metody
Metoda złożona jest ze słowa kluczowego member oraz identyfikatora (Identifier), np. this lub dowolnie innego, po którym występuje kropka, nazwa metody i przyjmowane parametry. Jest wiele możliwości tworzenia listy parametrów. W projekcie wtyczki lista argumentów przyjmuje postać jak podczas definiowania listy parametrów dla zwykłej luźnej funkcji. Zostało to opisane w poście dotyczącym luźnych funkcji.
Istnieją różne słowa kluczowe, których można użyć, definiując metody. Takim słowem jest np. static. Oznacza to, że tworzoną metodę można wywoływać bez stworzonej instancji obiektu.
Kolejnym słowem kluczowym jest abstract. Zasada jest podobna jak w przypadku obiektowych języków programowania. Podobnie jest ze słowem kluczowym virtual. Te słowa kluczowe mają zastosowanie podczas korzystania z dziedziczenia. Więcej szczegółów na temat dziedziczenia zostanie opisanych w poście związanym ze związkiem dziedziczenia.
Przykłady użycia operacji
Na rysunku 2 przedstawiona została klasa/typ z dwoma operacjami. Pierwszą operacją jest statyczna właściwość o nazwie Operation1. Jest ona publiczna. Kolejnym elementem w sekcji Operations jest metoda o nazwie Operation2. Przyjmuje ona trzy argumenty. Pierwszym argumentem jest a, drugim b, a trzecim jest krotka z dwoma elementami (aa,bb). Typem zwracanym dla tej metody jest podstawowy typ int.
Poniżej na rysunku 3 ustawione są właściwości w oknie właściwości dla elementu Operacja1.
Na rysunku 4 ustawione są właściwości dla elementu Operacja2. Jest to metoda.
Kod źródłowy wygenerowany na podstawie klasy z rys. 2 wygląda tak jak na listingu 5. Po sekcji do można zobaczyć właściwość Operation1 oraz metodę Operation2. Właściwość ma zdefiniowane obydwa akcesory. Użytkownik może dopisać logikę dla elementu set i get. Logikę dla metody użytkownik może dopisać ręcznie lub może skorzystać z elementu CodeBlock w celu dowiązania ciała metody. Zostało to przedstawione w postach opisujących m.in. CodeBlock.
type TypeClassElement () = do //to do static member public Operation1 with get() = //get-function-body and set(value) = //set-function-body member this.Operation2 a b (aa,bb) : int =
Listing 5: Wygenerowany kod źródłowy dla metody i właściwości w klasie
Właściwości, które można ustawiać dla elementów dodawanych do sekcji Operations (rys. 3 i 4), dzielą się na kilka kategorii:
- Właściwości ustawiane dla metod (ang. Methods) Methods Settings
- Właściwości ustawiane dla właściwości (ang. Properties) Properties Settings
- Wspólne właściwości dla dwóch elementów Common Settings
Właściwości, które można ustawić dla pierwszej kategorii Properties Settings, są następujące:
- Modyfikator dostępu
- Rodzaj akcesora
W tej części do dyspozycji mamy dwie właściwości. Pierwsza z nich w modelu nazwana jest Operation Accessibility. Szczegółowy opis można znaleźć wyżej w części Właściwość w tym poście.
Dla operacji, która jest właściwością, należy określić, jakiego rodzaju akcesory chcemy zastosować. Można to ustawić w polu Properties Accessors. Do wyboru mamy cztery możliwości. Pierwsza z nich to none, czyli żaden. Kolejną ewentualnością jest set, gdy chcemy mieć możliwość przypisywania wartości do właściwości. Inną opcją jest get, czli właściwość tylko do odczytu. Ostatnią możliwością jest ustawienie all, czyli get i set jednocześnie. Jest to zgodne z definicją właściwości opisaną wyżej w tej części postu oraz zgodne ze specyfikacją języka programowania F#.
W drugiej kategorii Methods Settings można ustawiać następujące właściwości:
- Czy metoda jest typu inline
- Lista parametrów metody
Właściwość ustawiająca metodę na inline nazwana jest w modelu Is Inline.
Parametry metody można ustawić we właściwości Name Type List. Robi się to podobnie jak w przypadku ustawiania parametrów dla luźnych funkcji.
W części wspólnej, czyli w kategorii Common Settings, właściwości możliwe do ustawienia są następujące:
- Rodzaj operacji
- Atrybuty dla operacji
- Rodzaj operacji
- Typ zwracany
- Nazwa operacji
We właściwości Member Kind do ustawienia możliwe są cztery opcje: abstract, default, none, override oraz static. Ustawia się je zgodnie z przeznaczeniem, do czego dana funkcjonalność operacji ma służyć.
Kolejną właściwością jest Operation Attributes, w której można ustawić atrybuty, te zgodne z frameworkiem .NET. Są umieszczane przed definicją metody lub właściwości w kodzie źródłowym w nawiasach kwadratowych.
Ważną właściwością tego elementu jest Operation Kind. Tutaj użytkownik definiuje, czy chce tak naprawdę wygenerować w kodzie źródłowym metodę (Method), czy właściwość (Property). Gdy wybrana jest opcja Property, podczas generowania kodu źródłowego brane są pod uwagę właściwości ustawione w sekcji Common Settings oraz Properties Settings. Gdy wybrana jest opcja Method, wtedy należy ustawiać właściwości w sekcjach Common Settings oraz Method Settings.
Właściwość, w której można ustawiać nazwę operacji, nazywa się Name. Powinna być zgodna ze specyfikacją języka F#.
Ostatnia właściwość to Return Type, w której można ustawić zwracany typ dla metod lub zwracany typ dla sekcji get w przypadku właściwości.
W przyszłych wersjach wtyczki do Microsoft Visual Studio 2010 będzie można stworzyć nowe właściwości lub lepiej je pogrupować, aby modelowanie stało się jeszcze łatwiejsze i wygodniejsze.