C# - historia (C# 9.0)
10 July, 2021 - 3 min read
Prześledźmy, jakie nowinki były wprowadzane w kolejnych wersjach C# (wybór) - w odwrotnie chronologicznej kolejności, najpierw wersja 9.0.
Rekordy
Skrótowa składnia dla klas, które jedynie przechowują dane. Innymi słowy - zamiast rozwlekłego:
public class Student {
public string FirstName {get; private set;}
public string LastName {get; private set;}
public Student(string firstName, string lastName){
FirstName = firstName;
LastName = lastName;
}
}
Możemy to zapisać tak:
public record Student(string FirstName, string LastName);
P.S. Zwróć uwagę na to, że zmienne w rekordzie są PascalCase - w ten sposób uzyskamy właściwości o "klasycznym" case'ingu. P.P.S. Rekordy mogą dziedziczyć tylko po rekordach - i na odwrót (tylko przez rekordy mogą być dziedziczone).
Init-only setters
W powyższym, pierwotnym, przykładzie ze studentem cały "myk" polegał na tym, żeby uniemożliwić modyfikację właściwości, tj. żeby nadać wartość tylko raz, podczas inicjalizacji obiektu. W tej chwili możemy jednak napisać w klasie Student
metodę, która, już po zainicjalizowaniu obiektu, będzie modyfikować wartość którejś z właściwości. Oczywiście, można temu zapobiec - po prostu mieć pole oznaczone jako readonly
, a właściwość tylko jako getter odczytujący z tegoż pola. To by było jednak już zupełnie rozwlekłe. Stąd w C# 9.0 wprowadzono nowe słowo kluczowe: init
. Zobaczmy je w akcji:
public class Student {
public string FirstName {get; init;}
public string LastName {get; init;}
public void ChangeFirstName(string firstName){
FirstName = firstName; // <-- TO SIĘ NIE SKOMPILUJE!!!
}
}
W ten sposób otrzymujemy klasę, która też jest podobna do rekordu - ale inicjalizujemy ją inaczej:
var student = new Student {
FirstName = "John",
LastName = "Smith"
};
student.FirstName = "Johnny"; // <-- TO SIĘ NIE SKOMPILUJE!!!
Po zainicjalizowaniu obiektu nie ma opcji, żeby zmienić wartość właściwości: czy to z zewnątrz, czy z wewnątrz klasy.
P.S. Oczywiście przykład z blokowaniem zmiany personaliów studenta może być mylący. W normalnym życiu przecież możliwa jest taka zmiana: od zwykłej omyłki na zmianie stanu cywilnego studenta kończąc. :)
Pomijanie typu przy słowie kluczowym new
W sytuacji, gdy kompilator potrafi domyślić się typu, wystarczy, że zawołamy samo new()
, bez podawania typu, np. przy deklaracji pola:
private List<string> list = new();
Albo gdy przekazujemy parametr do metody:
public class Car{
public Car(Engine engine){}
}
public class Engine{
public bool IsDiesel {get; init;}
}
var dieselCar = new Car(new(){IsDiesel=true});
To może ułatwić życie przy późniejszych refaktoryzacjach - zwłaszcza wtedy, gdy dokonujemy nie zmiany nazwy klasy (to nam załatwią narzędzia dziś dostępne), ale gdy np. rozbijamy jakąś klasę na dwie: nie musimy ręcznie poprawiać wszystkich wywołań konstruktorów (o ile oczywiście nie zmienią się parametry...).