Wprowadzenie

W poprzednim artykule dotyczącym C# 8.0 skupiliśmy się na (można by powiedzieć kosmetycznej zmianie) dotyczacej interpolacji ciągów. Artykuł możecie znaleźć po tym linkiem: C# 8.0 - interpolacja ciągów

W tej części poruszymy podobną zmianę – jedni powiedzą, że to nic istotnego, dla drugich będzie to swojego rodzaju udogodnienie – pozwala (moim zdaniem) na poprawę czytelności kodu.

Typy docelowe

Większość z nas nie lubi pisać zbyt długiego kodu. Jeżeli to jest nasz kod – "nic wielkiego". Problem pojawia się, gdy musimy przejść do interpretacji (długiego) kodu napisanego przez członków naszego zespołu lub osoby już nie pracujące w danej firmie. Dlatego, jako inżynierowie oprogramowania staramy się upraszczać kod tak bardzo jak to tylko możliwe.

C# od 2008 roku (C# 3.0) pozwala na deklarowanie zmiennych lokalnych bez definiowana typu docelowego. Służy do tego słowo kluczowe var:

Przypadek taki jak poniżej:

CarMaintenanceBusinessObject carBO = new CarMaintenanceBusinessObject(profile, CarMaintenace.Mode.NewCar, CarMaintenace.View.Dashboard);
Może być znacznie uproszczony:
var carBO = new CarMaintenanceBusinessObject(profile, CarMaintenace.Mode.NewCar, CarMaintenace.View.Dashboard);
Zdaje sobie również sprawę z tego, że var wywołuje zawsze goracą debatę wśród programistów. Osoby, które są przeciwko często mówią, że tylko leniwy programista korzysta z tego słowa kluczwego. Inni korzystają ze skróconego zapisu ale nie za każdym razem – są przypadki, kiedy chcą od razu widzieć jaki to typ. W tej cześci artykułu nie będzie skupiać się na tych rozważaniach – w niedługim czasie postaram się napisać osobny (szczegółowy artykuł) na ten temat. (Wówczas ta sekcja zostania zaktualizowana).

Wracając jednak do tematu tego wpisu...

Patrząc na uproszczony zapis jesteśmy od razu stwierdzić jaki jest typ obiektu carBO - nie ma potrzeby definicji wprost typu docelowego. Osobą początkującym słowo kluczowe var może kojarzyć sie z językiem JavaScript. To jednak błędne skojarzenie ponieważ kompilator języka C# zastąpi słowo kluczowe var właściwym typem – jest to tzw. lukier składniowy (czasami polskie tłumaczenia nie brzmią najlepiej...).

C# 8.0

Język w najnowszej odsłonie wprowadza inny sposób realizacji powyższego zadania pozwalając na zmniejszenie ilości pisanego kodu. Inicjalizacja może zostać „pominięta" jeżeli jesteśmy w stanie wywnioskować z kodu sposób użycia. Jednym z podstawowych przykładów jest inicjalizacja pola wewnątrz klasy lub struktury:

internal class Presentation
{
    // Poprzednia wersja języka zwracała błąd w przypadku poniższego zapisu
    private readonly static object _synbObj = new ();

    public Presentation()
    {
        Console.WriteLine("Wywołanie konstruktora");
    }
}

Przejdźmy do innego przykładu. Skupimy się na konstrukcji ConcurrentDictionary, która jest przeznaczona dla scenariuszy wielowątkowych. Używając tej konstrukcji nie trzeba stosować blokady w kodzie, aby dodać lub usunąć elementy z kolekcji. Należy jednak pamiętać, że pobranie wartości jest dostępne dla jednego wątku a drugi może dokonać natychmiastowej aktualizacji nadając temu samu kluczowi inną wartość:

// Pamiętajcie o przestrzeni nazw: using System.Collections.Concurrent;
private readonly static ConcurrentDictionary Cities = new ConcurrentDictionary();
Powyższy zapis może zostać skrócony:
private readonly static ConcurrentDictionary Cities = new ();

Kolejnym przykładem jest możliwość pominięcia powielania typu, gdy chcemy wykonać inicjalizację pola. To samo tyczy się inicjalizacji wewnątrz pola oraz gdy wykonujemy kolejną inicjalizację pola. Spójrzmy jednak na poprzednią wersję języka i przykład słownika:

Dictionary<string, List<string>> myDicionary = new Dictionary<string, List<string>>()
{
    { "item1", new List<string>() { "1", "2", "3" }},
    { "item2", new List<string>() { "3", "2", "1" }}
};
Korzystając z nowej konstrukcji języka możemy napisać nieco mniej kodu:
Dictionary<string, List<string>> myDicionary = new()
{
    { "item1", new () { "1", "2", "3" }},
    { "item2", new () { "3", "2", "1" }}
};

Ostanim przykładem jest użycie metod wywołujących w których możemy wywnioskować typ docelowy. Poprzednia wersja języka:

// Pamiętajcie o przestrzeni nazw: using System.Xml;
XmlReader.Create(reader, new XmlReaderSettings() { IgnoreWhitespace = true, IgnoreComments = false });
Typ docelowy XmlReaderSettings() może zostać wywnioskowany z powyższej konstrukcji dlatego nasz zapis możemy nieco uprościć korzystając z C#:
// Pamiętajcie o przestrzeni nazw: using System.Xml;
XmlReader.Create(reader, new() { IgnoreWhitespace = true, IgnoreComments = false });

Podsumowanie

Kolejny krótki wpis dotyczący C# 8.0 - typ razem skupiliśmy się na omówieniu typów docelowych. Mam nadzieje, że powyższy wpis wraz z przykładami w dwóch wersjach okaże się dla Was pomocny a sami zacznicie korzystać z nowych funkcjonalności jakie udostępnia ta wersja języka C#.