Читать книгу Programowanie funkcyjne w języku C#. Jak pisać lepszy kod - Enrico Buonanno - Страница 19
1.2.2 Elementy funkcyjne w C# 6 i C# 7
ОглавлениеC# 6 i C# 7 nie są tak rewolucyjne jak C# 3, ale obejmują wiele drobniejszych elementów języka, które w sumie dają dużo większe możliwości i bardziej idiomatyczną składnię do funkcyjnego kodowania.
UWAGA Większość funkcji wprowadzonych w C# 6 i C# 7 oferuje lepszą składnię, ale nie nowe funkcje. Jeśli używacie starszej wersji C#, możecie stosować wszystkie techniki pokazane w tej książce (wymaga to tylko trochę więcej pisania). Jednak nowsze funkcje znacznie poprawiają czytelność, co sprawia, że programowanie w stylu funkcyjnym jest bardzie atrakcyjne.
Te elementy możemy zobaczyć w działaniu na poniższym listingu.
using static System.Math;
public class Circle
Używanie static umożliwia niekwalifikowany dostęp do elementów statycznych System.Math, jak PI i Pow
{
public Circle(double radius)
=> Radius = radius;
public double Radius { get; }
Automatyczna właściwość tylko do odczytu może zostać ustawiona tylko w konstruktorze
public double Circumference
=> PI * 2 * Radius;
public double Area
Właściwość z wyrażeniem
{
get
{
double Square(double d) => Pow(d, 2);
Funkcja lokalna to metoda zadeklarowana w innej metodzie
return PI * Square(Radius);
}
}
public (double Circumference, double Area) Stats
Składnia krotek w C# 7 z nazwanymi elementami
=> (Circumference, Area);
}
Listing 1.4 Funkcje C# 6 i C# 7 istotne dla FP
Import elementów statycznych za pomocą using static
Użycie deklaracji using static w C# 6 pozwala nam na import statycznych elementów klasy (w tym przykładzie klasy System.Math). W rezultacie, w tym przykładzie możemy wywołać elementy Math o nazwach PI i Pow bez dalszej przestrzeni nazw:
using static System.Math;
public double Circumference
=> PI * 2 * Radius;
Dlaczego jest to ważne? W FP wolimy funkcje, których zachowanie zależy tylko od ich argumentów wejściowych, gdyż możemy wnioskować na ich temat i testować je oddzielnie (w odróżnieniu od instancji metod, których implementacja zwykle wpływa na instancje zmiennych). Te funkcje są implementowane jako statyczne metody w C#, zatem biblioteka funkcyjna w C# będzie się składać przede wszystkim z metod statycznych.
Deklaracja using static pozwala nam na łatwiejsze użycie takich bibliotek i choć nadużywanie może prowadzić do zanieczyszczenia przestrzeni nazw, to rozsądne wykorzystanie pozwala tworzyć czysty, czytelny kod.
Łatwiejsze typy niemutowalne z automatycznymi właściwościami typu tylko do odczytu
Gdy deklarujemy automatyczne właściwości tylko do odczytu, jak Radius, kompilator domyślnie deklaruje pole zapasowe (backing field) readonly. W wyniku tego właściwościom tym można przypisać wartość tylko w konstruktorze lub w miejscu zadeklarowania:
public class Circle
{
public Circle(double radius)
=> Radius = radius;
public double Radius { get; }
}
Automatyczne właściwości tylko do odczytu ułatwiają definicje typów niemutowalnych, które zobaczymy bardziej szczegółowo w rozdziale 9. Klasa Circle pokazuje, że ma tylko jedno pole (pole pomocnicze w Radius), które jest readonly, więc po jego utworzeniu Circle nie może go już zmienić.
Bardziej zwięzłe funkcje z użyciem wyrażeń w treści (expression-bodied)
Własność Circumference jest deklarowana za pomocą treści wyrażeniowej wprowadzonej przez =>, zamiast zwykłej treści instrukcji w {}:
public double Circumference
=> PI * 2 * Radius;
Zauważmy, jak jest to zwięzłe w porównaniu z własnością Area!
W FP mamy tendencję do pisania wielu prostych funkcji, często jednowierszowych, a następnie składamy z nich bardziej złożone funkcje. Metody jako wyrażenia pozwalają nam na wykonanie tego przy minimalnym narzucie składniowym. Jest to szczególnie widoczne, gdy chcemy napisać funkcję, która zwraca funkcję – coś co będziemy często robić w tej książce.
Składnia z wyrażeniami w treści została wprowadzona w C# 6 dla metod i właściwości, a potem została uogólniona w C# 7 na konstruktory, destruktory, gettery oraz settery.
Funkcje lokalne
Pisanie wielu prostych funkcji oznacza, że wiele funkcji będzie wywoływanych tylko z jednego miejsca. C# 7 pozwala nam na jawne zadeklarowanie metod w zakresie innych metod. Na przykład metoda Square jest zadeklarowana w zakresie funkcji zwracającej wartość Area:
get
{
double Square(double d) => Pow(d, 2);
return PI * Square(Radius);
}
Lepsza składnia dla krotek
Lepsza składnia dla krotek jest jedną z najważniejszych cech języka C# 7. Pozwala nam na łatwe tworzenie i używanie krotek, a co najważniejsze, na przypisywanie znaczących nazw ich elementom. Na przykład własność Stats zwraca krotkę typu (double, double) i podaje znaczące nazwy, poprzez które mamy dostęp do jej elementów:
public (double Circumference, double Area) Stats
=> (Circumference, Area);
Krotki są ważne w FP ze względu na tendencję do podziału zadań na bardzo małe funkcje. Może się okazać, że będziemy mieli typ danych, którego jedynym celem będzie przechwycenie informacji zwracanej przez jedną funkcję, która z kolei jest oczekiwana na wejściu innej funkcji. Niepraktyczne jest definiowanie dedykowanych typów dla takich struktur, które nie odpowiadają żadnym istotnym abstrakcjom z danej domeny. Tu właśnie jest miejsce na krotki.