środa, 5 stycznia 2022

Ewolucja C# from 1.0 to 10.0 - szybkie przypomnienie


C# i platforma .net
Programy napisane w C# uruchamiane są w .NET Framework, integralnym składniku systemu Windows, który zawiera CLR(Common Language Runtime) i jednolity zbiór bibliotek klas.  

CLR to środowisko uruchomieniowe, które wykonuje kod wyrażony w CIL(Common Intermediate Language). Zaś CLS(Common Language Specification, w skrócie CLS) określa podzbiór CLR, który kompilatory powinny obsługiwać, aby być kompatybilnymi z językami .NET.  

Kod źródłowy napisany w języku C# jest skompilowany w języku pośrednim (IL lub inaczej CIL), który jest zgodny ze specyfikacją interfejsu wiersza polecenia. Kod CIL i inne zasoby, są przechowywane na dysku w pliku wykonywalnym zwykle z rozszerzeniem .exe lub .dll.

Ponieważ kod CIL produkowany przez kompilator języka C# jest zgodny z CTS(Common Type Specification), kod CIL generowany z C# mogą współdziałać z kodem, który został wygenerowany z wersji .NET, Visual Basic, Visual C++ lub ponad 20 innymi językami zgodnymi z CTS. 





Wersję języka C#

Do ustawienia odpowiedniej wersji języka C# potrzeba też wybrać odpowiedni .NET framework i odpowiedni VS.


Najnowszy kompilator języka C# określa domyślną wersję języka na podstawie docelowej struktury lub platform projektu. Wybór wartości domyślnej gwarantuje, że używasz najnowszej wersji języka zgodnej z docelową platformą. Ten wybór domyślny gwarantuje również, że nie jest używany język, który wymaga typów lub zachowania środowiska uruchomieniowego niedostępnego w platformie docelowej.

Język C# 8.0 jest obsługiwany tylko w programie .NET Core 3.x i jego nowszej wersji.

 

C# w wersji 1.0

Główne funkcje języka C# 1.0 obejmuje:

C# w wersji 1.2

C# w wersji 1.2 dostarczane było z programem Visual Studio 2003.
Drobne ulepszenia jak: kod generowany w pętli foreach wywołuje Dispose na IEnumerator kiedy ten IEnumerator implementuje IDisposable.


C# w wersji 2.0

Główne funkcje języka C# w wersji 2.0, wydanego w 2005 r. wraz z Visual Studio 2005:
Inne funkcje języka C# w wersji 2.0 dodaje funkcje do istniejących funkcji:
  • Metody pobierającej/ustawiającej oddzielne ułatwień dostępu
  • Delegaty
  • Klasy statyczne
  • Delegate interface


C# w wersji 3.0

 C# w wersji 3.0 wydane pod koniec 2007, wraz z programu Visual Studio 2008.

Spójrzmy na niektóre główne funkcje w tej wersji:

 

C# wersji 4.0

Następna wersja wprowadzają interesujące nowe funkcje:

C# w wersji 5.0

Poniżej przedstawiono listę najważniejszych funkcji:


C# w wersji 6.0

 Poniżej przedstawiono niektóre z nich:
Inne nowe funkcje obejmują:
  • Await in catch/finally blocks
  • Default values for getter-only properties

C# w wersji 7.0

Poniżej przedstawiono nowe funkcje:
  • Out variables - definiowanie zmiennych out w jednej linijce.
    int.TryParse("83", out int numericResult2)
  • Konstrukcja Tuples - Nie trzeba już wprost określać jaki typu i wartości.
    var tuple = (1893, "Year");
    var model = (Year: 1983, Name: "Year");

  • Dekonstrukcja Tupli - dopasowywanie wartości od nazw zmiennych
    var tuple = (1893, "Year");
    (int year2, string name2) = tuple;

  • Pattern matching - daje name możliwość sprawdzenia warunku wartości, a następnie przetworzenie tej wartości, jeśli warunek jest prawdziwy. Rozszerza dwie konstrukcje języka o pattern matching: is a także case.
    Do dyspozycji mamy następujące wzorce
    • constant patterns - wzorzec testuje czy wejście jest równe wyrażeniu c 
    • type patterns - sprawdza czy wejście jest typu T i jeśli tak jest wartość z wejścia podstawia pod nową zmienną x typu T 
    • var patterns - ustawia wartość z wejścia pod nową zmienną x o typie takim jak wejście
            if (!(code is string packageCode)) return;
            if (code is null) return;
  • Local functions - możliwość deklarowania funkcji na potrzeby pojedynczej metody.
  • Ref locals and returns - możliwość deklarowania zmiennych po referencji i zwracania wartości poprzez referencję.
  • ValueTask<T> - można zwracać typ strukturalny. ValueTask ma być używany w przypadku, gdy już podczas wywołania metody asynchronicznej, znana jest zwracana wartość
  • Discards - Przy dekonstrukcji, gdy parametrów będzie bardzo dużo lub jakiś parametr nie będzie nam potrzeby to możemy go pominąć używając _
    var tuple = (2, "Day");
    (var day, _) = tuple;

  • Binary Literals and Digit Separators - wprowadzeniu nowego prefiksu: ”0b” lub ”0B”.
    var employeeNumber = 0b00100010; // binarny odpowiednik liczby 34
    int largeNaturalNumber = 345_2_45;
    int largeNaturalNumber_oldFormat = 345245; // dokładnie ta sama liczba jak powyżej

  • Throw expressions - możemy użyć instrukcji throw wewnątrz, np. wyrażenia warunkowego.
    string arg = args.Length >= 1 ? args[0] : throw new ArgumentException("You must supply an argument");
  • Expression bodied features - możliwość skrócenia pisania konstruktorów, destruktorów oraz getterów i setterów.
    public Vehicle(int fuel) => _fuel = fuel;
    ...
    set => _fuel = value;
    ...


https://www.plukasiewicz.net/Artykuly/WhatIsNewInCSharp7
https://blogprogramisty.net/lista-zmian-c-7-0-7-1-7-2-7-3/

C# w wersji 7.1

Dostępne są następujące nowe funkcje językowe w tej wersji:
  • async Main method - możemy zamienić metodę Main na asynchroniczną wersję i dzięki temu uzyskać punkt wejścia do metod asynchronicznych.
    static async Task Main()
    {
        await SomeAsyncMethod();
    }

  • Default literal expressions - aby wydobyć w łatwy sposób domyślną wartość dla typu.
    Dla typów referencyjnych to null a dla typów wartościowych to zależy. Kompilator sam domyśli się typu dla instrukcji default i ustawi go jako jak obowiązujący typ.
    int number = default; // 0
  • Domyśle nazwy dla Tupli - możemy polegać na kompilatorze, który stworzy domyślne nazwy z nazw zmiennych przekazanych do Tupli.
    int Number = 1983;
    var Name = "Przemek";
    var tuple = (Name, Number);
    Console.WriteLine(tuple.Name); // Przemek

https://blogprogramisty.net/nowosci-w-c-7-1/

C# w wersji 7.2

Dostępne są następujące nowe funkcje języka w tej wersji:
  • Słowo kluczowe in - parametr jest przekazywany przez referencję ale nie jest modyfikowany przez metodę.
  • Składania readonly struct - pozwala na stworzenie zmiennej struktury. Czyli taka struktura powinna być przekazywana do metody poprzez słowo kluczowe in.
  • Modyfikator ref readonly - Użyj dla metody returns, aby wskazać, że metoda zwraca swoją wartość przez odwołanie, ale nie zezwala na zapis do tego obiektu.
  • Zadeklaruj typy, aby wskazać, że typ struktury uzyskuje bezpośredni dostęp do pamięci zarządzanej ref struct i zawsze musi być przydzielony stos.
  • Zmiany w parametrach nazwanych - możliwość podawania argumentów do metody bez wymieniana ich nazwy zaraz po argumentach nazwanych, gdy kolejność argumentu(nie nazwanego) się zgadza.
    TestFunc(a: 2, "b", c: new Point());
    private void TestFunc(int a, string b, Point? c = null) {...}
    Argument b jest podany bezpośrednio i jest po argumencie nazwanym i jego kolejność się zgadza więc kompilator może się domyślić który to jest.
  • Podkreślenia w literałach liczbowych dla wartość hex i binarnych:
    const int Sixteen =   0b_0001_0000;
  • Modyfikator dostępu private protected - widoczność elementów będzie ograniczała się do klasy bazowej i do klasy dziedziczącej w tej samej bibliotece.
https://blogprogramisty.net/nowosci-w-c-7-2/

C# w wersji 7.3

Dostępne są następujące nowe funkcje języka w tej wersji:
  • Wyrażenie fixed - umożliwia zabezpieczenie w kodzie nie zarządzanym aby dany fragment pamięci nie był przenoszony przez GC.
    private unsafe void FixedPoint()
    {
       int p_new = s.myFixedField[5];
    }

  • Można już przypisywać do zmiennej lokalnej typu ref inna zmienną typu ref.
    ref VeryLargeStruct refLocal = ref veryLargeStruct; // initialization
    refLocal = ref anotherVeryLargeStruct; // reassigned, refLocal refers to different storage.

  • stackalloc - można skorzystać z inicjalizacji tablicy podczas jej deklaracji (wydajność). Tworzy region pamięci na stosie i zwraca wskaźnik do początku tej pamięci. Pamięć przydzielona do stosu jest automatycznie usuwana po wyjściu z zakresu, w którym została utworzona. Powinien być używany tylko do optymalizacji wydajności
    int* pArr = stackalloc int[3] {1, 2, 3};
  • Wyrażenie fixed wspiera więcej typów - każdy obiekt, który posiada metodę, która zwraca typ ref lub ref readolny może być użyty razem z instrukcją fixed (wydajność dla kodu niezarządzanego).
  • Wsparcie Typli dla opratorów == i != - od teraz Tuple można porównywać
  • Atrybut field dla get-ów i set-ów.
    [field: SomeThingAboutFieldAttribute]
    public int SomeProperty { get; set; }

  • Naprawa błędu z przeciążeniem paramentu "in" - zastosowanie w metodzie operatora "in" powodował dwuznaczność w przeciążeniach. To znaczny, kompilator nie wiedział, którą metodę ma wybrać. Do przeciążenia będzie metoda 1 (bez in), jeśli będziemy chcieli wybrać drugą metodę musimy użyć słowa in.
    static void M(S arg);
    static void M(in S arg);

  • Zmienne typu out w konstruktorach.
    public B(int i, out int j) {...}
  • Nowe opcje kompilatora - nowe opcje postały aby wspierać różne scenariusze DevOps (-publicsign, -keyfile, -keycontainer, -pathmap).
https://blogprogramisty.net/nowosci-w-c-7-3 
 
 


C# w wersji 8.0

Język C# 8.0 jest obsługiwany na .NET Core 3.x i .NET Standard 2.1.
C# 8 can be used with the . NET Framework and other targets older than . NET Core 3.0/. NET Standard 2.1 in Visual Studio 2019 (or older versions of Visual Studio if you install a Nuget package).
  • Nullowalne typy referencyjne np. string
    string? st = null;
  • Domyślna implementacja w interfejsie (prymitywne wielodziedziczenie) - prawie jak klasa abstrakcyjna
  • Wyrażenie Switch będzie można napisać dużo krócej (dla nieskomplikowanej logiki) bez powielania słów "break", "case" i "result".
    iNum switch
    {
        RandomNum.One => return "1",
            RandomNum.Six => return "6",
            _              => throw new Exception("invalid number value"),
    };

  • Uproszczenie zapisu "using".
    using var file = new System.IO.StreamWriter("WriteLines2.txt");
  • Dwa nowe typy: Index i Range - ^ (d końca sekwencji) oraz .. (zakres)
    sequence[sequence.Length] to teraz sequence[^0]
    [0..sequence.Length] to teraz [0..^0]
    foreach (var item in languages[2..5]) - od 2 indeksu (3 element) do 4 indeksu
    foreach (var item in languages[..^1]) - Pokaż mi elementy oprócz ostatnieg

  • Async streams (IAsyncEnumerable<T>) - ułatwienie w konsumpcji/tworzeniu strumieni danych wytwarzanych przez IoT czy chmurę.
  • Wprowadzony pattern matching w C# 7 będzie mógł zawierać inny pattern matching.  
  • Tworząc nowe elementy, typ wykrywany będzie na podstawie kontekstu, co pozwoli na ominięcie nazwy typu w deklaracji
  • Modyfikator readonly można zastosować do elementów struktury (optymalizacja).
    public struct getSqure
    {
        public readonly int output => Math.Pow(A,B);
        public override string ToString() => $"The answer is : {output} ";
    }

  • Null Coalescing Assignment - Nowy operator ??=. Jego zadaniem jest zadeklarować daną zmienną, jeżeli jest ona niezadeklarowana, czyli ma NULL.
    lotteryNumbers.Numbers ??= new List<int>();
  • stackalloc as an Expression
    Span<int> num = stackalloc[] { 20, 30, 40 };
    var index = num.IndexOfAny(stackalloc[] { 20 });
    Console.WriteLine(index);  // output: 0
  • Ulepszenie ciągu interpolowanego - $@"" lub @$"" są poprawne, oznacza to, że możesz rozpocząć string albo @lub $. We wcześniejszej wersji C# @ jest dozwolona dopiero po $.
  • Lokalne funkcje statyczne - możesz utworzyć funkcję lokalną jako static.
    int Add()
    {
        return Sum(A);
        static int Sum(int val) => val + 3;
    }
  • Operator zerowego łączenia - operator o nazwie as null -operator koalescencyjny. Ten operator jest oznaczony jako ??=. Operator zwraca wartość lewego operandu, jeśli nie jest null, w przeciwnym razie zwraca wartość operandu po prawej stronie.
    (lstNum ??= new List<int>()).Add(100);

https://www.dobreprogramy.pl/@djfoxer/nowosci-w-c--duze-zmiany-ale-nie-dla-wszystkich,blog,92211
https://docs.microsoft.com/pl-pl/dotnet/csharp/whats-new/csharp-version-history#c-version-80
https://www.codeproject.com/Articles/1265802/New-Features-of-Csharp-8


C# w wersji 9.0

Język C# 9 został wydany wraz z .NET 5.
  • Typy rekordów - słowo kluczowe record służy do definiowania typu odwołania, który zapewnia wbudowaną funkcję hermetyzacji danych. Rekordy mogą być modyfikowalne, ale są przeznaczone głównie do obsługi niezmiennych modeli danych. Rekordy obsługują dziedziczenie. Rekordy różnią się od klas, ponieważ typy rekordów używają równości opartej na wartości. Dwie instancje rekordów są równe, jeśli wartości wszystkich ich właściwości i pól są równe.
    public record Person(string FirstName, string LastName);
    • Metoda "ToString" zwraca wartości recordu.
      Console.WriteLine(personRecord.ToString());
      //PersonRecord { Name = Moody, Surname = Blues }
    • użyj słowa "with" by tworzyć instancje rekordów na podstawie istniejących instancji rekordów.
      var person = new Person("Tom", "Twain");
      var another = person with { Name = "Finn" };
      Console.WriteLine(another);
      //Person { Name = Finn, Surname = Twain }
  • Init only setters - możesz tworzyć init akcesory zamiast set akcesorów dla właściwości i indeksatorów. Pozwala na modyfikację wartości tylko w czasie inicjalizacji obiektu.
    public struct WeatherObservation { public DateTime RecordedAt { get; init; } }
  • Top-level statements - pozwala pominąć zarówno namespace, class, jak i metodę Main.
    using System;
    Console.WriteLine("Hello World!");

  • Pattern matching enhancements -
    • Conjunctive "and" patterns require both patterns to match
    • Disjunctive "or" patterns require either pattern to match
    • Negated "not" patterns require that a pattern doesn't match
      public static bool IsLetterOrSeparator(this char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z') or '.' or ',';
  • Native sized integers "nint" oraz "nuint" - natywne liczby całkowite, nint oraz nuint. Są one wyrażane przez podstawowe typy System.IntPtr i System.UIntPtr . Natywne liczby całkowite definiują właściwości dla MaxValue lub MinValue.
  • Target-typed new expressions - kiedy podajemy wprost typ, tworząc zmienną, możemy pominąć nazwę typu.
    MyInstanceType value = new MyInstanceType();

    MyInstanceType value = new();
     
  • static anonymous functions - zezwalaj na modyfikator „static” w lambdach i metodach anonimowych 
  • Obsługa rozszerzenia GetEnumerator
    • foreach może rozpoznać GetEnumerator jako metodę rozszerzającą:
      public static class TupleExtensions
      {
          public static IEnumerator<T> GetEnumerator<T>(this ValueTuple<T, T, T, T> tuple)
          {
              yield return tuple.Item1;
              ...
              yield return tuple.Item4;
          }
      }
      foreach(var item in (1, 2, 3, 4))
      { //1 2 3 4 }

    • można również iterować przez zakres
      public static IEnumerator<Index> GetEnumerator(this Range number)

      {
          for (Index i = number.Start; i.Value < number.End.Value; i = i.Value + 1)
          {
              yield return i;
          }
      }
      public static void Main()
      {
          foreach (var i in 1..5)
          { //1 2 3 4 }
      }

    • Odrzucenie parametrów funkcji anonimowych i lambda - jeśli nie potrzebujemy parametrów w wyrażeniu, możesz w ich miejscu zostawić podkreślenie.
      button1.Click += (_, _) => ShowNextWindow();
  • Lokalne atrybuty funkcji - możesz zastosować atrybuty do funkcji lokalnych.
    [Conditional("DEBUG")]
    static void PrintDebug()
    {
       Console.WriteLine("This is debug mode");
    }

  • Zaktualizowane metody częściowe - teraz mogą mieć wartość powrotną out parametrów oraz modyfikatory dostępu.
    Teraz można oddzielić pliki nagłówkowe i implementację, a także wykonać deklarację forward. Jeśli jednak metoda częściowa otrzymała modyfikator dostępu, projekt nie skompiluje się bez implementacji.
    public partial class Person
    {
        public string Surname { get; set; }
        public Person(string name, string surname)
        {
            Surname = surname;
        }
        public partial bool Speak(string line, out string text)
    }
    public partial class Person
    {
        public partial bool Speak(string line, out string text)
        {   ...
            return true;
        }
    }

https://docs.microsoft.com/pl-pl/dotnet/csharp/whats-new/csharp-version-history#c-version-9
https://nofluffjobs.com/blog/nowosci-w-c-9-0-przeglad-wybranych-ulepszen/
https://pvs-studio.com/en/blog/posts/csharp/0860/


C# w wersji 10

Język C# 10 jest obsługiwany na .NET 6.
  • Ulepszenia typów struktur i struktury rekordów (record struct) - Nowością jest to, że rekordów można użyć razem ze strukturami, co tworzy bardzo proste kontenery na dane z przewidywalnym zachowaniem.
    public record struct Point
    {
        public double X {  get; init; }
        public double Y {  get; init; }
        public double Z {  get; init; }
    }
    public readonly record struct Point(double X, double Y, double Z);

  • Pojawiła się też nowa składnia "!!" - po nazwie parametru w sygnaturze metody sprawi, że zostanie sprawdzona wartość tego parametru i zostanie rzucony wyjątek, jeżeli jest ona nullem.
    public SomeFunction(int id, SomeClass newObject!!)
    {...}

    zamiast
    public SomeFunction(int id, SomeClass newObject)
    {
      if (newObject == null)
      { throw new ArgumentNullException("newObject");} ...
    }

  • Wprowadzono też global using - będziesz mógł zdefiniować globalne przestrzenie nazw dla całego projektu. Ogólnie zaleca się utworzenie osobnego pliku, który będzie zawierał te importy, coś w rodzaju usings.cs . Użycie słowa kluczowego global C# eliminuje marnotrawstwo pionowe.
    global using System;
    global using System.Collections.Generic;

  • Deklaracja przestrzeni nazw o zakresie pliku - możliwość określenia namespace’u na początku pliku za pomocą słowa kluczowego namespace. Eliminuje marnotrawstwo poziome - ma na celu rozwiązanie problemu wcięć.
    namespace A;
    public class B
    { ... }

    zamiast
    namespace A {
      public class B {
      }
    }

  • Wzorce właściwości rozszerzonych - można odwoływać się do zagnieżdżonych właściwości lub pól we wzorcu właściwości.
    { Prop1.Prop2: pattern }
    zamiast
    { Prop1: { Prop2: pattern } }
  • Ulepszenia wyrażeń lambda
  • Ciągi interpolowane jako stałe - ciągi const, które mogą być inicjalizowane z użyciem interpolacji ciągów znaków, ale tylko wtedy, gdy same placeholdery również będą stałymi ciągami znaków.
    const string message = "Thank you for buying the book!";
    const string thankYouMessage = $"{message} Enjoy";

  • Typy rekordów można zapieczętować ToString() - mamy możliwość dodania modyfikatora sealed (uniemożliwia innym klasom dziedziczenie z klasy), gdy nadpisujemy ToString w rekordzie. Funkcja zapewnia, że wszystkie pochodne typy używają metody ToString zdefiniowanej we wspólnym typie rekordu podstawowego.
    public sealed string ToString()
    {
       return $"My name is {FirstName} {LastName}";
    }

  • Deklaracja i przypisanie w tej samej dekonstrukcji
    int x = 0;
    (x, int y) = point;

  • Atrybut CallerArgumentExpression - umożliwia bibliotekom tworzenie bardziej szczegółowej diagnostyki.
    public static void Validate(bool condition, [CallerArgumentExpression("condition")] string? message=null)
    {
        if (!condition)
        {
            throw new InvalidOperationException($"Argument failed validation: <{message}>");
        }
    }

https://bulldogjob.pl/news/1853-nowosci-w-net-6-i-c-10
https://dev.to/andreisfedotov/whats-new-in-c-10-new-features-of-c-10-15lo


Materiały:


 

Brak komentarzy:

Prześlij komentarz