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 SKOMPILUJEW 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 weekend1Fajne, 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.