TABLE OF CONTENTS:
O tym od czego zacząć tworzenie własnej aplikacji już pisaliśmy, jednak wciąż dostajemy wiele pytań o to, w czym tę aplikację webową czy desktopową najlepiej napisać, jakie technologie będą najlepiej oddawały dany koncept i jakich narzędzi użyć, żeby były one jak najbardziej optymalne. O wypowiedź poprosiliśmy jednego z naszych ekspertów, Tomasza Przedzińskiego. Tomek przygotował cykl wpisów na temat najpopularniejszych języków programowania, który naświetli poszczególne technologie, przybliży popularność języków programowania i podpowie, w czym pisać aplikację webową.
Wielu z nas śledzi od czasu do czasu index TIOBE, wyjątkowo uniwersalny wskaźnik zestawiający najpopularniejsze języki programowania. Jest bardzo użytecznym narzędziem, pokazującym nie tyle trend współczesnego rynku, co jego status quo w zakresie narzędzi i technologii, używanych przez programistów na całym świecie. TIOBE bardzo trafnie wskazuje, jakie języki programowania mają przyszłość, jednocześnie podpowiadając kierunki, w których warto rozwijać swoje umiejętności, ale też pokazuje, czego potrzebuje biznes.
Top 5 języków programowania w indeksie TIOBE, lipiec 2022. Źródło: www.tiobe.com
W rankingu na lipiec 2022 TIOBE zaznacza, że Python, C, C++ i C# są obecnie najlepszymi kandydatami na język roku 2022. Obecność najpopularniejszych języków Java¹, C i C++ w czołówce jest niezmienna od ponad 20 lat, podczas gdy szybko rosnąca popularność Pythona po raz pierwszy w jego karierze umiejscowiła go na pierwszym miejscu rankingu. Popularność języka C# ma swoje wzloty i upadki, jednak w ostatnich latach dość często widać jego obecność w Top 5.
Dlaczego warto go znać i co wyróżnia C# na tle innych popularnych języków programowania? O tym za chwilę.
Nie powinno być zaskoczeniem, że kandydatury wymienione przez TIOBE pokrywają praktycznie cały rynek usług programistycznych, a każda z nich zajmuje kluczowe miejsce w innym sektorze. O tym, który język programowania wybrać, często decyduje typ i przeznaczenie oprogramowania. Język C króluje w najniższych warstwach oprogramowania i w oprogramowaniu wbudowanym. C++ pokrywa szerokie spektrum zastosowań, choć skupia się głównie na backendzie i na rozwiązaniach, w których liczy się wydajność, uwzględniając zastosowania giełdowe, takie jak HFT (ang. High Frequency Trading) i Low-Latency Trading. Python, z kolei, jest nieocenionym narzędziem do szybkiego prototypowania rozwiązań i budowania frameworków testowych. Jest też bardzo popularny wśród osób zajmujących się uczeniem maszynowym. C# w połączeniu z frameworkiem .NET, zwłaszcza po wydaniu .Net Core, jest jedną z najczęściej używanych technologii w szeroko pojętych rozwiązaniach chmurowych, w aplikacjach webowych, mobilnych oraz desktopowych – w szczególności na system operacyjny Windows. Dobrze daje sobie radę w połączeniu z zyskującym coraz większą popularność IoT. Warto wspomnieć, że wszystkie największe silniki gier są napisane w C++, przy czym silnik Unity jest częściowo napisany w C#. Gry pisane na silnik Unity pisane są w C#, co znacząco przyczyniło się do popularyzacji tego języka².
Ideą przemawiającą za powstaniem C# było stworzenie wysokopoziomowego języka programowania podobnego w założeniach do Javy, a jednocześnie bliskiego w składni do C++. C# miał uprościć składnię C++ i dostarczyć narzędzia dostępne we współczesnych językach, stawiając na niezawodność i łatwy rozwój projektu. Popularność C# świadczy o tym, że przynajmniej częściowo ten koncept udało się zrealizować.
Program “Hello world!” w C#
C# jest statycznie typowanym językiem wysokiego poziomu. Podobnie jak Java, kompilowany jest do języka pośredniczącego, zwanego CL (Common Language). Do wykonania kodu potrzebna jest odpowiednia infrastruktura, taka jak np. platforma .NET, w ramach której jest on uruchamiany w CLR (Common Language Runtime), czyli maszynie wirtualnej działającej na podobnych zasadach co JVM (Java Virtual Machine).
Ze względu na swe mocne powiązanie z platformą .NET, bardzo często zamiast mówić, że pisze się w języku C# mówi się, że pisze się dla platformy .NET³. A to dlatego, że z pisaniem na platformę .NET związane jest wykorzystanie całego ekosystemu, który ona w sobie zawiera. Jest to m.in. wspomniane wcześniej środowisko uruchomieniowe, ale również zbiór bibliotek.
Ze środowiskiem .NET wiąże się wiele różnych terminów i detali, a sam proces kompilacji i uruchamiania kodu na tej platformie jest całkiem skomplikowany. Nie trzeba jednak ich znać, by zacząć w nim pracę. Warto zaznaczyć jednak najważniejszy punkt: większość projektów rozwijanych jest na platformę .NET Core albo .NET Framework. Ta pierwsza zorientowana jest na wydajność i multiplatformowość, a jej główne przeznaczenie to aplikacje webowe, mikroserwisy i aplikacje na urządzenia mobilne. .NET Framework ogranicza swoją implementację do platformy MS Windows⁴, w zamian za to umożliwiając łatwy rozwój aplikacji desktopowych i gier. Obu platform można użyć do pisania aplikacji webowych przy użyciu ASP.NET⁵. Te dwie platformy należy traktować odrębnie, gdyż kodu napisanego dla jednej nie da się uruchomić na drugiej. Ich część wspólna zwana jest .NET Standard.
Platforma .NET w pełni wykorzystuje możliwości, jakie daje uruchomianie kodu pośredniczącego. Mamy zatem:
Graficzna edycja okien, na przykład w Visual Studio, znacząco przyspiesza proces budowania aplikacji desktopowych na platformie .NET i sprawia, że jest ona częstym wyborem, gdy przychodzi stworzyć aplikację w sektorze finansowym. Często buduje się w niej też aplikacje mające służyć jako interfejs do zarządzania bazami danych w firmach. Z kolei ASP.NET jest wygodną alternatywą dla PHP czy Node.js do tworzenia serwisów WWW. ASP.NET jest ceniony za swoją niezawodność i skalowalność, dlatego budowane są w niej największe serwisy webowe, takie jak StackOverflow. Łatwość, z jaką projekty można hostować na platformach chmurowych, odciąża nie tylko developerów, ale i managerów od zajmowania się infrastrukturą, w jakiej osadzony jest projekt.
Microsoft zadbał o to, by platformy .NET można było wygodnie używać w rozwiązaniach chmurowych, a zwłaszcza w Microsoft Azure. Azure posiada szereg narzędzi ułatwiających tworzenie mikroserwisów i, co w mojej ocenie dużo ważniejsze, monitorowanie ich działania i poszukiwanie błędów, wliczając monitorowanie zapytań do baz danych, by ułatwić ich optymalizację.
C# jest językiem obiektowym⁶. Posiada wbudowane mechanizmy programowania asynchronicznego, reprezentowane m.in. przez słowa kluczowe async i await. Umożliwiają one efektywne programowanie z wykorzystaniem zarówno podejścia TAP (ang. Task-based Asynchronous Pattern), jak i EAP (ang. Event-based Asynchronous Pattern).
Coś, co wyróżnia C# na tle innych popularnych języków programowania, to wbudowany mechanizm tworzenia zapytań – LINQ (ang. Language Integrated Query). LINQ pozwala tworzyć zapytania, które można wykonać zarówno na bazie danych, jak i na kolekcji obiektów. Można przy tym używać płynnego interfejsu (ang. Fluent Interface) albo składni w stylu języka SQL. Zapytania są zoptymalizowane pod wykorzystywane przez projekty bazy danych (lub kolekcje) i pozwalają na leniwą ewaluację. Mogą być również zrównoleglone przy użyciu PLINQ (Parallel LINQ).
C# unika wielu problemów zaobserwowanych w innych językach. Ogromną zaletą, która mnie osobiście bardzo się podoba, jest przejrzystość błędów kompilacji. Intellisense, działający na przykład jako wtyczka do Visual Studio Code, zgłasza praktycznie każdy możliwy błąd. Naprawiwszy wszystkie, mam prawie stuprocentową pewność, że kod się skompiluje i uruchomi. Coś, czego w pracy z C++ bardzo, ale to bardzo brakuje.
Jednak wykorzystanie mechanizmu kompilacji do kodu pośredniczącego i wykonanie go przy pomocy środowiska uruchomieniowego, pociąga za sobą narzut w postaci obniżonej wydajności. Zoptymalizowany program w C++ wykona się szybciej od tego napisanego w C#. Z naciskiem na słowo „zoptymalizowany”, bo często ręcznie pisane rozwiązania w C++ są wydajniej zrealizowane w bibliotekach platformy .NET.
Tworzenie aplikacji internetowych i desktopowych z platformą .NET jest wyzwaniem ze względu na poziom jej złożoności. Wiele konceptów wymaga poznania detali działania tej platformy, by uniknąć typowych błędów spowalniających wykonywanie programu. Do niektórych podstawowych mechanizmów trzeba się po prostu przyzwyczaić, bo na przykład działają inaczej niż oczekiwałby programista.
Dla przykładu – struktury i klasy mają w C# inne przeznaczenie, więc są obsługiwane inaczej. Struktury powinny być używane do przechowywania obiektów typu POD (ang. Plain Old Data), dlatego przypisanie lub przekazanie ich jako argument funkcji tworzy ich kopię. Klasy są traktowane podobnie jak w języku Java – przekazywane są przez referencję. To detal, którego łatwo się nauczyć, ale nieraz potrafi zaskoczyć. Takich wyjątków w zachowaniu C#, w porównaniu do innych języków, jest zdecydowanie więcej.
Przykład typowego problemu w C# – powyższy kod wypisze ‘10, 20, 20 ,20’, co może zaskoczyć niektórych programistów. ‘Test1’ jest strukturą, więc w linii 14 zostanie wykonana jego kopia, podczas gdy ‘Test2’ jest klasą, więc w linii 23 zostanie stworzona nowa referencja do tej samej instancji klasy. Te różnice łatwo wyłapać, gdy widać definicję ‘Test1’ i ‘Test2’, ale w złożonym projekcie, gdy w grę wchodzą kolekcje różnych typów, trudniej śledzić typy manipulowanych obiektów i łatwo popełnić błąd.
Warto też pamiętać, że podobnie jak w języku Java, projekty napisane w .NET mają tendencję do tworzenia wielu warstw abstrakcji i potrafią być bardzo złożone, czego ze względów wydajnościowych unika się w projektach pisanych w C czy C++⁷. Próg wejścia w istniejący projekt jest dość wysoki i wymaga dużej wiedzy domenowej, zanim będzie można w nim efektywnie pracować.
Zaczęliśmy od końca, ale przejdziemy przez charakterystyki wszystkich kandydatów. Czy wyłonimy własnego zwycięzcę? Stay tuned, a tymczasem zostawiamy Was z krótkim zestawieniem podstawowych cech kandydatów, żeby zobrazować największe różnice między nimi.
C | C++ | Python | C# | |
Poziom abstrakcji | Niski | Wysoki | Wysoki | Wysoki |
Typ języka | Kompilowany | Kompilowany | Interpretowany | Zarządzany |
Główny paradygmat programowania | Proceduralny | Obiektowy | Proceduralny, funkcyjny, obiektowy (i inne) | Obiektowy |
Typowanie | Statyczne | Statyczne | Dynamiczne | Statyczne |
Refleksja | Brak | Bardzo ograniczona | Pełna | Pełna |
Zarządzanie pamięcią | Brak | Brak | GC | GC |
Obsługa wyjątków | Brak | Jest (można wyłączyć) | Jest | Jest |
Wsparcie dla programowania wielowątkowego | Brak | TAP, EAP, inne | TAP, EAP, inne | TAP, EAP, inne |
Najpopularniejszy manager pakietów | Brak | Conan | pip | NuGet |
Najpopularniejsze narzędzie do budowania projektu | make, CMake | CMake | (Niepotrzebne) | dotnet, mono |
Trudność nauczenia się języka | Łatwy | Trudny | Bardzo łatwy | Średni |
Praca w złożonych projektach | Bardzo trudna | Trudna | Średnio trudna | Średnio trudna |
Poszukiwanie błędów | Szalenie trudne | Trudne | Trudne | Średnio trudne |
Zestawienie kluczowych różnic pomiędzy językami C, C++, Python i C#.
Ostatnie dwie pozycje mocno zależą od sposobu organizacji projektu. Zawsze możemy trafić na taki, w którym poziomy trudności są odwrócone.