🔥 Darmowa Roadmapa .NET — kompletny plan nauki do pierwszej pracy. Pobierz i dołącz do Listy VIP →

Newsy .NET — .NET 11 RC1, unie w C# 15 i CS8509 (wrzesień 2026)

Newsy .NET wrzesień 2026 — .NET 11 RC1, unie w C# 15 i ostrzeżenie CS8509 zamiast błędu kompilacji

W pierwszym odcinku Newsów .NET napisałem, że w C# 15 brak jednego przypadku unii w switch to błąd kompilacji. We wrześniu wyszedł .NET 11 Release Candidate 1,
więc uruchomiłem to na prawdziwym SDK. Program się skompilował, wypisał dwie linijki i wywrócił się na trzeciej a kompilator przez cały czas wiedział, którego przypadku brakuje, i napisał to po imieniu.

To jest sprostowanie i zarazem pierwszy z ośmiu newsów z września. Każdy fragment
kodu w tym wpisie został uruchomiony na .NET 11 RC1 (11.0.100-rc.1.26425.128,
30.09.2026) wyniki poniżej to realny output konsoli, nie przepisana dokumentacja.
Pozostałe pozycje, zebrane z pierwotnych źródeł: paczka NuGet, która wykonuje swój kod w chwili otwarcia projektu, nowy certyfikat Microsoftu, dzień, w którym kończą się dwie wersje .NET naraz, i trzy zmiany w GitHub Copilot, których nikt nie włączał.

.NET 11 RC1: na produkcję już wolno, C# 15 domyślnie

8 września 2026 Microsoft wypuścił .NET 11 Release Candidate 1 z licencją
go-live oficjalnie można go używać na produkcji i liczyć na wsparcie. Druga
zmiana jest ważniejsza dla codziennej pracy: w RC1 C# 15 jest domyślną wersją
języka
 dla projektów na net11.0. Nie dopisujesz już  <LangVersion>preview</LangVersion> — unie, zamknięte hierarchie i etykietowany break działają od ręki. Sprawdziłem to wprost: żaden z plików poniżej nie ma flagi preview.

Finalna wersja wychodzi na .NET Conf, która startuje 10 listopada. Zapamiętaj tę
datę, wraca niżej, w dużo mniej przyjemnym kontekście.

Zasada: 
RC z licencją go-live to moment, w którym warto zacząć testy na własnym
kodzie, nie po premierze. To, co znajdziesz teraz, możesz jeszcze zgłosić.

Unie w C# 15 – składnia finalna jest inna niż w odcinku #1

W odcinku pierwszym pokazałem „przewidywaną składnię” unii: warianty zadeklarowane w klamrach, wewnątrz typu. Zaznaczyłem, że to faza projektowania. Zmieniło się — finalna unia wylicza typy, które już istnieją, a przypadki są zwykłymi rekordami:

Shape[] shapes = [new Circle(1), new Square(2)];

foreach (Shape s in shapes)
{
    Console.WriteLine($"{s.Value}: {Area(s):F2}");
}

static double Area(Shape s) => s switch
{
    Circle c => Math.PI * c.Radius * c.Radius,
    Square sq => sq.Side * sq.Side
};

public record Circle(double Radius);
public record Square(double Side);

public union Shape(Circle, Square);
Circle { Radius = 1 }: 3,14
Square { Side = 2 }: 4,00

Cała unia to ostatnia linijka. Circle i Square nie wiedzą, że do niej należą —
możesz ich używać także poza nią, co odróżnia unię od klasycznej hierarchii
dziedziczenia. switch nie ma gałęzi domyślnej (_ =>), bo kompilator zna zamknięty
zbiór przypadków. Jeśli wyrażenia switch z wzorcami typów są dla Ciebie nowe, zacznij od Switch w C# — od klasycznej instrukcji po pattern matching,
a o samych rekordach przeczytasz w Class, struct czy record w C#?.

Jeden szczegół, który wyszedł dopiero przy uruchomieniu: w Console.WriteLine stoi s.Value, nie samo s. Pierwsza wersja tego pliku miała {s} i wypisywała Shape: 3,14 — unia zamieniona na tekst podaje nazwę unii, a nie przypadku. Do tego, co jest w środku, sięgasz przez właściwość Value.

Zasada: 
unia łączy typy, które już masz. Nie przepisujesz klas pod nią, dopisujesz
jedną linijkę, która mówi, z czego się składa.

Pułapka #1: CS8509 to ostrzeżenie, nie błąd

A teraz właściwe sprostowanie. Dopisuję trzeci wariant, trójkąt do unii i do tablicy,
ale switch zostawiam bez zmian. Dokładnie tak, jak wygląda to w prawdziwym projekcie, gdzie takich switch-y jest dwadzieścia w różnych plikach:

Shape[] shapes = [new Circle(1), new Square(2), new Triangle(3, 4)];

foreach (Shape s in shapes)
{
    Console.WriteLine($"{s.Value}: {Area(s):F2}");
}

static double Area(Shape s) => s switch
{
    Circle c => Math.PI * c.Radius * c.Radius,
    Square sq => sq.Side * sq.Side
};

public record Circle(double Radius);
public record Square(double Side);
public record Triangle(double Base, double Height);

public union Shape(Circle, Square, Triangle);

Kompilacja:

warning CS8509: Wyrażenie switch nie obsługuje wszystkich możliwych wartości typu wejściowego (nie jest wyczerpujące). 
Na przykład nie jest uwzględniony wzorzec „Triangle”.

I uruchomienie:

Circle { Radius = 1 }: 3,14
Square { Side = 2 }: 4,00
Unhandled exception. System.Runtime.CompilerServices.SwitchExpressionException: Non-exhaustive switch expression failed to match its input.
Unmatched value was Shape.

Kompilator wie dokładnie, czego brakuje i buduje program. To jest ostrzeżenie
CS8509
, nie błąd. W odcinku #1 i w poście do niego napisałem „błąd kompilacji, nie
runtime exception”. Na RC1 jest odwrotnie: kompilacja przechodzi, a wyjątek leci
w runtime, na pierwszym trójkącie. Co gorsza, komunikat wyjątku mówi „Shape”, nie
„Triangle” z samego logu nie dowiesz się, którego przypadku zabrakło.

Uczciwie trzeba dodać, co unia jednak zmienia: przy zwykłej hierarchii z gałęzią _ =>
kompilator milczy całkowicie. Przy unii zna nazwę brakującego przypadku. Decyzję,
czy to zatrzymuje build, zostawia jednak Tobie.

Uruchom sam

Trójkąt w unii, brak trójkąta w switch

// (brak dyrektywy — domyślne ustawienia)
Shape[] shapes = [new Circle(1), new Square(2), new Triangle(3, 4)]; foreach (Shape s in shapes) { Console.WriteLine($"{s.Value}: {Area(s):F2}"); } static double Area(Shape s) => s switch { Circle c => Math.PI * c.Radius * c.Radius, Square sq => sq.Side * sq.Side }; public record Circle(double Radius); public record Square(double Side); public record Triangle(double Base, double Height); public union Shape(Circle, Square, Triangle);
Kliknij „dotnet run”. Wynik to prawdziwy output z .NET 11 RC1.

Zasada: 
kompilator C# rozróżnia „wiem, że to źle” od „nie pozwolę”.
Wyczerpujący switch w C# 15 jest w pierwszej kategorii
– dopóki sam go nie przesuniesz do drugiej.

Jedna linijka, która robi z tego błąd

Program w jednym pliku (ten sam dotnet run app.cs, o którym był odcinek #1) przyjmuje właściwości MSBuild dyrektywą #:property na górze pliku:

#:property WarningsAsErrors=CS8509

Z tą linijką ten sam kod kończy się tak:

error CS8509: Wyrażenie switch nie obsługuje wszystkich możliwych wartości typu wejściowego (nie jest wyczerpujące). Na przykład nie jest uwzględniony wzorzec „Triangle”.

Kompilacja nie powiodła się. Napraw błędy kompilacji i uruchom ją ponownie.

W zwykłym projekcie to samo ustawiasz w .csproj:  <WarningsAsErrors>CS8509</WarningsAsErrors>
w PropertyGroup. Jeśli zespół ma już  <TreatWarningsAsErrors>true</TreatWarningsAsErrors>,
dostajesz to za darmo, razem z każdym innym ostrzeżeniem. Jeśli nie ma, CS8509 jest
pierwszym kandydatem
, zanim w kodzie pojawi się pierwsza unia.

Zasada: 
wybierz jedno ostrzeżenie, które na pewno oznacza błąd, i zrób z niego błąd.
CS8509 przy uniach to najtańsza taka decyzja w C# 15.

Etykietowany break – już bez flagi preview

Drugi temat z odcinka #1 też jest w RC1 i też nie wymaga preview. Przeszukujemy macierz 3 × 3 w poszukiwaniu piątki:

outer:
for (int i = 0; i < matrix.Length; i++)
{
    for (int j = 0; j < matrix[i].Length; j++)
    {
        checkedCells++;
        if (matrix[i][j] == target)
        {
            Console.WriteLine($"Znaleziono {target} w [{i}][{j}]");
            break outer;
        }
    }
}
Znaleziono 5 w [1][1]
Sprawdzone komorki: 5

Pięć sprawdzonych komórek, nie dziewięć – break outer kończy obie pętle naraz, bez flagi bool sprawdzanej w warunku zewnętrznej pętli i bez goto. Nowa reguła stylu IDE0410 sama wskazuje w istniejącym kodzie miejsca, gdzie flagę albo goto da się zastąpić etykietą.

closed — zamknięte hierarchie, brat unii

Trzecia funkcja C# 15, której w odcinku #1 nie było. Słowo closed przed klasą (albo
rekordem) bazową mówi: dziedziczyć po mnie można tylko w tym samym projekcie:

public closed record Shape;
public record Circle(double Radius) : Shape;
public record Square(double Side) : Shape;

Kompilator zna więc wszystkie przypadki i switch (ten sam co wyżej, bez gałęzi
domyślnej) jest dla niego wyczerpujący. Wynik identyczny jak przy unii. closed
jest niejawnie abstrakcyjne – nie połączysz go z sealed, static ani jawnym
abstract. Jeśli zastanawiasz się, kiedy w ogóle sięgać po klasę bazową, a kiedy po
interfejs, zajrzyj do Klasa abstrakcyjna vs interfejs w C#.

Kiedy co? 

Unia — gdy łączysz typy, które już istnieją i nie mają wspólnego przodka.
closed — gdy projektujesz własną hierarchię od zera. Oba mają tę samą cechę przy
brakującym przypadku: CS8509, ostrzeżenie (sprawdzone na tym samym SDK — wyjątek z closed mówi przynajmniej Unmatched value was Square { Side = 2 }, bo rekord wypisuje swoją zawartość).

Zasada: 
closed to „ta lista przypadków jest skończona” powiedziane kompilatorowi,
a nie komentarzowi w kodzie.

Otworzyłeś projekt — i cudzy kod już się wykonał

1 września 2026 Maarten Balliauw, od lat piszący o NuGecie i bezpieczeństwie .NET,
opublikował szczegółowy rozbiór: jak zbudować atak na łańcuch dostaw w .NET. Nie po to, żeby atakować — żeby pokazać, gdzie są drzwi. Cztery mechanizmy, które paczka NuGet może wykorzystać, dzielą się na dwie grupy.

Przy buildzie (i przy otwarciu projektu):

  • MSBuild .targets z folderu build/ albo buildTransitive/ paczki — MSBuild
    dołącza go do Twojego projektu sam i może wykonać go przed kompilacją.
  • Generator źródeł — kod paczki uruchamiany w środku kompilatora.

Oba działają także w buildzie w tle, który IDE robi samo po otwarciu solution.
Nie uruchomiłeś programu, nie kliknąłeś „Build” — otworzyłeś projekt.

Przy starcie aplikacji:

  • Inicjalizator modułu — rusza przy pierwszym użyciu biblioteki, przed Twoim kodem.
  • Startup hook — zmienna DOTNET_STARTUP_HOOKS ładuje dowolną bibliotekę przed Main.

Autor pokazuje też wariant, który sprawdza zmienne środowiskowe typowe dla serwerów CI (CI, TF_BUILD) i uruchamia się wyłącznie tam — na laptopie programisty nie widać niczego.

Kiedy to się wykonuje?

Cztery drzwi dla kodu z paczki NuGet

Wybierz, co robisz z projektem, do którego ktoś dodał paczkę. Podświetlą się mechanizmy, które w tej chwili mogą uruchomić jej kod.

Gdzie to się dzieje:
MSBuild .targetsz folderu build/ paczki — MSBuild dołącza go sam
Generator źródełkod paczki w środku kompilatora
Inicjalizator modułuprzy pierwszym użyciu biblioteki
Startup hookDOTNET_STARTUP_HOOKS — przed Main
Zacznij od kroku 1.

Obrona, którą da się wdrożyć od ręki: plik blokady pakietów z  RestorePackagesWithLockFile i tryb RestoreLockedMode na CI (restore nie może po cichu wziąć innej wersji), mapowanie źródeł pakietów w nuget.config (paczka o danej nazwie może przyjść tylko z Twojego feedu) i regularny przegląd zależności przechodnich. Sam autor mówi uczciwie: to nie daje pewności, tylko podnosi koszt ataku.

Zasada: 
dodanie paczki NuGet to wpuszczenie cudzego kodu do procesu budowania,
nie tylko do aplikacji. Traktuj dotnet add package jak instalację programu.

Pułapka #2: NU3034 po zmianie certyfikatu Microsoftu

Od 23 września 2026 Microsoft podpisuje swoje paczki NuGet nowym certyfikatem.
Stare paczki zachowują stary podpis. Zmiana dotyczy Cię tylko wtedy, gdy w nuget.config masz listę zaufanych autorów (trustedSigners) albo sprawdzasz podpisy komendą dotnet nuget verify z odciskami certyfikatów. Jeśli tak, a nie dopiszesz nowego odcisku – instalacja nowych paczek Microsoftu kończy się błędem NU3034.

Poprawka to jedna komenda: dotnet nuget trust certificate Microsoft <odcisk>
–algorithm SHA256 z odciskiem z ogłoszenia Microsoftu (link w źródłach niżej).
Stare wpisy zostawiasz.

Uwaga:
ogłoszenie podaje wariant `dotnet nuget trust author … –algorithm SHA256`, ale na SDK 10.0.401 kończy się on błędem „nierozpoznany argument `–algorithm`” – `trust author` przyjmuje ścieżkę do paczki, nie odcisk. Wariant z `certificate` dopisuje do `nuget.config` dokładnie ten wpis `<author name=”Microsoft”>`, który pokazuje ogłoszenie (sprawdzone 01.10.2026).

I przypomnienie z odcinka #1: 1 listopada 2026 wygasają wszystkie stare klucze API
NuGet.org. Jeśli publikujesz paczki z pipeline’u, sprawdź to teraz, nie 31 października.

10 listopada: premiera .NET 11 i koniec .NET 8 i .NET 9

Tego samego dnia, kiedy wychodzi .NET 11, kończy się wsparcie .NET 8 i .NET 9 – obu naraz. .NET 8 to wersja LTS z listopada 2023 (trzy lata wsparcia). .NET 9 miał
krótkie wsparcie, które Microsoft wydłużył do 24 miesięcy — i dlatego oba kończą się
jednego dnia.

Aplikacje na .NET 8 i 9 dalej będą działać. Po 10 listopada nie dostaną jednak żadnej
poprawki bezpieczeństwa (Microsoft zastrzega tylko ewentualną ostatnią łatkę tego dnia,
jeśli wyjdzie krytyczny problem). Wrześniowe łatki z 8 września – .NET 10.0.12,
9.0.20 i 8.0.31
 – należą do ostatnich dla ósemki i dziewiątki. Zalecenie Microsoftu:
.NET 10, wsparcie do listopada 2028.

Twój kalendarz

Które terminy z tego odcinka dotyczą Ciebie?

Zaznacz, co pasuje do Twojej sytuacji. Lista niżej zostawi tylko Twoje daty — liczone od dzisiejszej.

    Zasada: 
    „działa” i „jest wspierane” to dwie różne rzeczy. Po 10 listopada .NET 8 działa dokładnie tak samo – tylko każda nowa podatność zostaje w nim na zawsze.

    GitHub Copilot: trzy zmiany, których nie włączałeś

    W odcinku #1 był szok cenowy po przejściu Copilota na rozliczanie tokenów. We wrześniu ciąg dalszy – ceny bez zmian, ale trzy rzeczy zmieniają się same (changelog GitHub z 28 sierpnia):

    • 28 września — code review Copilota ma nowy poziom domyślny: Balanced zamiast Lite. Mocniejszy model i cały kontekst repozytorium, a więc więcej zużytych kredytów. Chcesz starego zachowania – ustaw Lite jawnie.
    • 28 września — czat na github.com i w GitHub Mobile połączony z agentem w chmurze. Historia rozmów jest trzymana przez całe życie konta, a nie jak dotąd 28 dni.
    • 1 października — w planach Business i Enterprise opłata za stanowisko z góry,
      zanim użytkownik dostanie dostęp.

    Szybka runda

    • C# 15: with(...) w wyrażeniu kolekcji – pierwszy element podaje argumenty
      konstruktora, np. porównywarkę StringComparer.OrdinalIgnoreCase dla HashSet<string>.
    • C# 15: indeksery w rozszerzeniach – w bloku extension dopiszesz własny
      this[int] do typu, którego nie jesteś właścicielem.
    • Visual Studio 18.10 – debuger podświetla w if z &&/|| ten warunek, który
      rozstrzygnął wynik, a „Attach to Process” widzi kontenery Podmana jak Dockera.
    • EF Core 11 – dotnet ef database update NazwaMigracji --add tworzy migrację i od razu ją nakłada, jedną komendą.

    Ściągawka — 8 newsów z września

    #NewsKonkret
    1.NET 11 RC18.09, go-live, C# 15 domyślny dla net11.0
    2Unie w C# 15public union Shape(Circle, Square); — składnia inna niż w #1
    3CS8509brak przypadku = ostrzeżenie; błąd tylko z WarningsAsErrors
    4break outer i closedbez flagi preview; closed = hierarchia tylko w projekcie
    5Łańcuch dostaw.targets i generatory działają już przy otwarciu projektu
    6NuGetnowy certyfikat 23.09 (NU3034), stare klucze API wygasają 1.11
    710 listopadapremiera .NET 11, koniec wsparcia .NET 8 i 9
    8Copilot28.09 Balanced i historia na zawsze, 1.10 płatność z góry

    Zadanie na ten tydzień

    Masz chwilę na eksperyment: pobierz .NET 11 RC1, zapisz kod z trójkątem jako
    app.cs i uruchom dotnet run app.cs. Przeczytaj CS8509 na własne oczy. Potem
    celowo dopisz na górze #:property WarningsAsErrors=CS8509 i zobacz, jak ten sam kod przestaje się kompilować. Na koniec dopisz brakującą gałąź dla Triangle — i sprawdź, że ostrzeżenie znika.

    Nie masz czasu: sprawdź, na jakim .NET stoją Twoje projekty (TargetFramework
    w .csproj). Każdy na net8.0 albo net9.0 ma termin: 10 listopada.

    Podsumowanie

    • .NET 11 RC1 (8.09) ma licencję go-live; C# 15 jest w nim językiem domyślnym, bez flagi preview.
    • Unia w C# 15 wylicza istniejące typy: public union Shape(Circle, Square);. Do wartości sięgasz przez Value.
    • Niepełny switch na unii to ostrzeżenie CS8509 i wyjątek w runtime — błędem staje się dopiero z WarningsAsErrors.
    • Paczka NuGet może wykonać kod przy samym otwarciu projektu (.targets, generatory) — blokada wersji i mapowanie źródeł to minimum.
    • 10 listopada: premiera .NET 11 i koniec wsparcia .NET 8 i 9 tego samego dnia.

    Pełny odcinek z animowanym przebiegiem tego, jak error zamienia się w warning, jest na kanale YouTube — w opisie są wszystkie źródła. Poprzednia partia newsów, z pierwszą (nieaktualną już) wersją składni unii: Newsy .NET — sierpień 2026.

    🎓 Jeśli chcesz przejść C# od podstaw po poziom, na którym takie newsy od razu wiesz, gdzie przyłożyć – zacznij od darmowej roadmapy Junior .NET Developer
    albo kursu Od Zera do .NET Developera.

    Zobacz

    Źródła: 
    .NET 11 RC1 ·
    Co nowego w C# 15 ·
    M. Balliauw — atak na łańcuch dostaw w .NET ·
    Certyfikat NuGet Microsoftu ·
    Koniec wsparcia .NET 8 i 9 ·
    Copilot — changelog 28.08 ·
    EF Core 11

    Mariusz Jurczenko
    Mariusz Jurczenko
    Senior .NET Developer · 10+ lat doświadczenia komercyjnego

    Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

    Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

    🚀 Co dalej?

    Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

    Dodaj komentarz

    czytanie to początek

    Zamień wiedzę w umiejętności

    Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

    Pobieram roadmapę →