C# - historia (C# 8.0)

2021-07-20

To jest kontynuacja przeglądu historii wersji C# (tu znajdziesz poprzedni artykuł).

Readonly members

Metoda w strukturze może być oznaczona jako readonly - w ten sposób oznaczamy ją jako “instance-agnostic”, czyli po pierwsze nie może ona zmieniać stanu struktury, a po drugie - każde odwołanie do elementów struktury (tylko odczyt), będzie skutkować ostrzeżeniem.

public struct WeatherMeasurement {
    public double Temperature {get; init;}

    public override readonly string ToString() => $"Temperature was {Temperature}."; // <-- TU BĘDZIE WARNING

    public readonly void ChangeTemperature(double newTemperature) => Temperature = newTemperature; // <-- TU BĘDZIE BŁĄD KOMPILACJI
}

Trzeba zapamiętać, że w przypadku “ostrzeżeniowym” kompilator pod spodem robi taki “myk”: tworzy kopię struktury i na tej kopii wywołuje tę metodę/właściwość. W ten sposób oryginalna instancja nie jest dotykana, ale to dodaje ukryty narzut.

P.S. Tego readonly nie można użyć dla metod klasy - tam działa tylko na polach.

Domyślne implementacje w interfejsach

Dotychczas interfejsy mogły mieć tylko same sygnatury metod/właściwości. Jeśli mieliśmy wiele implementacji interfejsu, ale jakiś wspólny kod, to trzeba było wprowadzać pomiędzy te implementacje a interfejs abstrakcyjną klasę “superbazową” i tam umieszczać tę wspólną logikę. Natomiast od C# 8.0 można to zrobić od razu w interfejsie, bez sztucznej pośredniej klasy bazowej. Na przykład:

public interface IOrder {
    decimal NetTotal {get;}
    decimal VatPercentage {get;}
    decimal GrossTotal => NetTotal * (1 + VatPercentage/100);
}

public class Order : IOrder {
    public decimal NetTotal {get;init;}
    public decimal VatPercentage {get;init;}
}

W tej sytuacji możemy korzystać z właściwości IOrder.GrossTotal, mimo że w klasie Order nie została ona zainicjalizowana. Ważne jest jednak to, że dostęp do niej mamy tylko, kiedy instancja klasy Order jest użyta w kontekście IOrder - inaczej nie będziemy mieli do niej dostępu. Porównaj:

IOrder order1 = new Order(){ NetTotal = 100, VatPercentage = 22};
Console.WriteLine(order1.GrossTotal); // <-- TU DZIAŁA

var order2 = new Order(){ NetTotal = 100, VatPercentage = 22};
Console.WriteLine(order2.GrossTotal); // <-- TO SIĘ NIE SKOMPILUJE

W drugim użyciu var jes tylko syntactic sugar - zmienna order2 jest typu Order.

Pattern matching w switchach

Pattern matching w połączeniu ze switch umożliwia bardzo zgrabny zapis:

public string GetFuelString(EngineType engineType) => engineType switch {
    EngineType.Diesel => "ON",
    EngineType.Petroleum => "95",
    EngineType.LPG => "LPG",
    _ => throw new ArgumentOutOfRangeException(message: "Unrecognized engine type", paramName: nameof(engineType))
};

Co więcej, można też “matchować” za pomocą warunków określonych na właściwościach, np.:

public bool HasDiscount(Customer customer) => customer switch {
    {IsVIP: true} => true,
    {Age: <18} => true,
    {Age: >65} => true,
    {FirstName: "Adam", LastName: "Małysz"} => true
    _ => false
};

W mocy pozostaje jednak ograniczenie switcha: porównywana wartość musi być stałą.

IAsyncEnumerable

O tym isałem w oddzielnym wpisie, więc odsyłam po szczegóły do niego.

Using bez klamerek

Mniej wcięć dzięki temu, że using może być “inline’owy” - kompilator sam “ogarnie”, że “scope” sięga do końca “aktualnego scope’u”.

public bool WriteToFile(){
    using var stream = new StreamWriter("file.txt");

    // <-- TU JESTEŚMY "WEWNĄTRZ" USINGA

    return true;
}

Operacje na indeksach

C# 8.0 zapewnia nam zgrabniejszy dostęp do fragmentów tablic i konkretnych pozycji, np.:

var weekdays = new[]{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};

var sunday = weekdays[^1];

var weekend1 = weekdays[5..7]; // <-- TO SAMO CO weekend2
var weekend2 = weekdays[^2..^0]; // <-- TO SAMO CO weekend1

Fajne, tylko czasem może być kłopotliwe - jak tutaj przy zmiennej weekend1: nie dość, że na dzień dobry jest mindfuck, bo piątek to 4, to jeszcze drugi koniec takiego przedziału nie wchodzi do niego (dlatego jest indeks 7, którego nie ma w tablicy), czyli przedział jest jednostronnie domknięty; ale z drugiej strony jak odliczamy indeksy od końca (przykład z weekend2), to zasada jest odwrotna.


Robert Skarżycki - zdjęcie profilowe

Pisanina, której autorem jest Robert Skarżycki - programista .NET, mąż szczęśliwej żony, rodzic
moje bio
mój Twitter
mój LinkedIn
moje szkolenia i warsztaty

© 2022, Built with Gatsby & passion