WIADOMOśCI Z BRANżY

.NET 11 Preview 3: Recenzja programisty

.NET 11 osiąga trzeci podgląd około sześciu miesięcy przed planowanym GA w listopadzie 2026, a w przeciwieństwie do Preview 1 i 2, które były głównie rurowaniem, Preview 3 to ten, gdzie można faktycznie poczuć kształt wydania.

Runtime Async przestaje być eksperymentem "preview-feature gate", JIT zyskuje kolejną porcję darmowych optymalizacji, ASP.NET Core otrzymuje Zstandard od razu, a unie typów C# 15 - funkcja językowa, o którą pytano od ponad dekady.

To nie jest wydanie LTS, .NET 11 to kolejna wersja Standard Term Support, z 24 miesiącami wsparcia, co już zmienia kalkulacje dotyczące tego, czy warto się aktualizować. Poniżej znajduje się to, co naprawdę warto twojej uwagi jako dewelopera, i gdzie nadal są niedoskonałości.

Unie typów C# 15: Duża zmiana

Jesli pisales biblioteki OneOf<T1, T2>, tworzac wlasne hierarchie rekordow typu sealed lub po prostu zazdrosciles deweloperom F#, to jest news dla ciebie. C# 15 wprowadza slowo kluczowe union, ktore deklaruje, ze wartosc jest dokladnie jednym z okreslonego zbioru typow, przy czym kompilator wymusza kompletna obsluge przypadkow. Typy unijne pojawiły się w Podglądzie 2; Podgląd 3 doskonali wsparcie IDE wokół nich.

Podejście zespołu C# jest bliższe unii typów niż rozróżników unii F#: typy członkowskie to istniejące typy, które zdefiniowałeś osobno, a nie przypadki tagów zagnieżdżone wewnątrz deklaracji unii. Unia to w zasadzie struktura, która obudowuje obiekt i ogranicza, co może do niej wejść. Z punktu wywolania czuje sie to jak natywne - implicit conversion z typow czlonkow do unii, wyczerpujace wyrazenia switch nad Pet.Value, i nie potrzeba domyslnego ramienia, gdy kompilator widzi wszystkie przypadki.

[Union]
public partial struct Pet : IUnion
{
    public Pet(Dog dog);
    public Pet(Cat cat);
    public Pet(Bird bird);
}

string Describe(Pet pet) => pet.Value switch
{
    Dog d  => $"Dog named {d.Name}",
    Cat c  => $"Cat ({c.Color})",
    Bird b => $"Bird, {b.Species}",
    // no default - compiler knows the set is closed
};
[Union]
public partial struct Pet : IUnion
{
    public Pet(Dog dog);
    public Pet(Cat cat);
    public Pet(Bird bird);
}

string Describe(Pet pet) => pet.Value switch
{
    Dog d  => $"Dog named {d.Name}",
    Cat c  => $"Cat ({c.Color})",
    Bird b => $"Bird, {b.Species}",
    // no default - compiler knows the set is closed
};
<Union>
Public Partial Structure Pet
    Implements IUnion

    Public Sub New(dog As Dog)
    End Sub

    Public Sub New(cat As Cat)
    End Sub

    Public Sub New(bird As Bird)
    End Sub
End Structure

Function Describe(pet As Pet) As String
    Return pet.Value Select Case
        Case d As Dog
            Return $"Dog named {d.Name}"
        Case c As Cat
            Return $"Cat ({c.Color})"
        Case b As Bird
            Return $"Bird, {b.Species}"
        ' no default - compiler knows the set is closed
    End Select
End Function
$vbLabelText   $csharpLabel

Zalety sa realne: zamkniete typy, brak niewaznych stanow, wyczerpywalnosc w czasie kompilacji zamiast NotImplementedExceptionat o 3 nad ranem. Dodaj rekina do unii, a każdy switch na Pet rozświetla się ostrzeżeniami, dopóki go nie obsłużysz.

Wady są również realne i warto je wskazać w każdej rzetelnej recenzji. Eksponowana wartościowa właściwość Object Value to zapach - trwa otwarta dyskusja na GitHubie o ukrywaniu jej za czymś bardziej bezpiecznym typowo. Publiczne konstruktory implicitATYPE typu akceptowalnego przez unię, co nie jest ani odkrywalne, ani jawne. Interop F# jest nierozwiązany (dwa modele są fundamentalnie różne). Szersza historia wyczerpującości nadal ma luki: zamknięte hierarchie i zamknięte enumeracje, dwa wnioski, które byłyby obrazem dopełnającym, nadal są propozycjami. Unie same w sobie są świetne. Unie plus zamknięte enumeracje plus zamknięte hierarchie byłyby generacyjną zmianą. Nie jesteśmy jeszcze tam.

Runtime Async V2 i ulepszenia JIT

Runtime Async to cicha przepisa .NET dotycząca wyglądu async/await rzeczywiście wykonuje się. Zamiast tego, żeby kompilator C# emitował klasę state-machine na metodę asynchroniczną, samo środowisko wykonawcze zarządza zawieszeniem i wznowieniem. Widoczny zwrot: czystsze ślady stosu, mniejsze alokacje i debugger, który nie zmusza do przewijania przez ramki MoveNext, aby znaleźć swój własny kod.

W Preview 3, Runtime Async znosi wymaganie EnablePreviewFeatures. Wciąż przelaczasz przelacznik funkcji - <Features>runtime-async=on</Features> - ale nie musisz juz dla kazdego wywolania API przechodzic do terytorium wersji preview. Wsparcie NativeAOT i ReadyToRun wylądowało w tej wersji podglądu, co zamyka lukę między scenariuszami JIT i AOT. Obiekty kontynuacji są bardziej agresywnie ponownie wykorzystywane, a zmienne lokalne, które się nie zmieniały, nie są zapisywane podczas zawieszenia. W ścieżkach kodu ciężkich w async - pomyśl o pipeline Kestrel lub pracowniku zapytań EF Core - to znaczący spadek presji alokacyjnej.

JIT zdobyło swoją zwykłą porcję "twój istniejący kod jest teraz szybszy, nie rób nic":

  • Wyrażenia switch z wieloma celami, takie jak x jest 0 lub 1 lub 2 lub 3 lub 4, teraz składają się w kontroli bezgałęziowe.
  • Kontrole zakresu dla wartości[^1] + patternów wartości[^2] są bardziej agresywnie eliminowane, a powszechne przypadki i + cns < len w pętlach składają się czysto.
  • Rzutowanie unsigned-int-to-float i unsigned-int-to-double jest szybsze na sprzęcie x86 przed AVX-512 - nieczęste, ale realne, jeśli jesteś na starszych maszynach.

Uzytkownicy WebAssembly dostaja ladowanie ladunkow WebCIL bezposrednio w srodowisku uruchomieniowym, lepsze symbole debug i float[] / Span<float> / ArraySegment<float> marshaling bez dodatkowego obciazenia zwrotkami. Żadne z tych nie są indywidualnie dramatyczne, ale razem są rodzajem komponowania pracy, która sprawia, że Blazor WASM czuje się mniej jak kompromis.

Haczyk to dolna granica sprzętowa. .NET 11 podnosi minimalne wymagania zestawu instrukcji na x86/x64 i Arm64. Apple Silicon i większość Linux SBC są w porządku - gotowy cel ReadyToRun na Arm64 dodaje jedynie LSE - ale bardzo stary sprzęt x86 nie spełnia wymagań. Przeprowadź audyt swojej floty, zanim założysz aktualizację na miejscu.

ASP.NET Core i Blazor

Zstandard jest tutaj glownym punktem programu. ASP.NET Core teraz obsługuje zstd zarówno dla kompresji odpowiedzi, jak i dekompresji żądań, i jest to domyślnie włączone, gdy dodajesz middleware. Konfiguracja jest w takim kształcie, jakiego byś się spodziewał:

builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
    options.CompressionOptions = new ZstandardCompressionOptions { Quality = 6 };
});
builder.Services.AddResponseCompression();
builder.Services.AddRequestDecompression();
builder.Services.Configure<ZstandardCompressionProviderOptions>(options =>
{
    options.CompressionOptions = new ZstandardCompressionOptions { Quality = 6 };
});
Imports Microsoft.Extensions.DependencyInjection

builder.Services.AddResponseCompression()
builder.Services.AddRequestDecompression()
builder.Services.Configure(Of ZstandardCompressionProviderOptions)(Sub(options)
    options.CompressionOptions = New ZstandardCompressionOptions With {.Quality = 6}
End Sub)
$vbLabelText   $csharpLabel

Dla API obsługujących JSON lub tekst dla klientów już obsługujących zstd - coraz powszechniej w ekosystemach mobilnych i powiązanych z gRPC - to jest mierzalna wygrana pod względem przepustowości bez zależności od biblioteki zewnętrznej. Jest to także, co ważne, wkład społecznościowy, a nie wewnętrzny projekt Microsoft, co jest zdrowym sygnałem.

Blazor's Virtualize<TItem> wreszcie przestaje zakladac, ze kazdy wiersz ma ta sama wysokosc. To była długotrwała irytacja: każda lista z zmienną zawartością - komentarze, wiadomości na czacie, cokolwiek z tekstem zawijanym - wymagała ręcznych obejść. Teraz komponent mierzy elementy w czasie rzeczywistym. Wydanie Preview 3 rowniez naprawia kilka bledow w Blazor: null reference w Virtualize, wykrywanie kontenera przewijania z overflow-x, szablon Web Worker w opublikowanych aplikacjach WASM, TempData przypadki graniczne lazy loading, i wyciek IJSObjectReference w ResourceCollectionProvider. Indywidualnie małe, zbiorczo to rodzaj czyszczenia, który sygnalizuje dojrzewanie frameworka poza fazę "nowość przy każdym wydaniu".

Kestrel również zaczyna przetwarzać żądania HTTP/3 bez czekania na strumień kontrolny i ramkę SETTINGS, co zmniejsza opóźnienie pierwszego żądania w nowych połączeniach. Jeśli mierzyłeś HTTP/3 P99s i zauważyłeś dziwne ogony zimnego startu, to jest twoja poprawka.

.NET MAUI

MAUI w Preview 3 koncentruje się głównie na zamykaniu luk, które sprawiały, że wyglądała na wersję beta przez zbyt długi czas. Komponent Map zyskuje klasteryzacje pinow, niestandardowe ikony pinow, niestandardowe stylizowanie JSON i zdarzenia klikniecia na kolach, wielokatach i liniach lamanych - wszystko, co potrzebuja prawdziwe produkcyjne interfejsy mapowe i co wczesniej wymagalo niestandardowych obslugiwaczy dla kazdej platformy. Dostępny jest teraz wbudowany LongPressGestureRecognizer bez konieczności tworzenia własnego. Domyślne deklaracje przestrzeni nazw XAML są teraz domyślnie włączone, co skraca szablony na początku każdego pliku.

Paritytet platform sie poprawia: Permissions.PostNotifications jest teraz zaimplementowany na iOS (wczesniej byl dostepny tylko dla Androida), a Android zyskuje wsparcie preview dla Android 17 i poziomu API 37.

Uczciwa ocena: to stabilne, rozsądne iteracje, a nie reinwencja. MAUI w 2026 roku jest w znacznie lepszej sytuacji niż MAUI w 2023, ale jeśli odeszłeś od niego wcześniej, samo Preview 3 nie przyciągnie cię z powrotem. Jeśli już korzystasz z MAUI, to są dokładnie te zmiany QoL, które chcesz.

SDK, CLI i .NET watch

To sekcja, w której te małe rzeczy sumują się. Kilka z nich, które, moim zdaniem, rzeczywiście zmieniają codzienny przepływ pracy:

.NET sln może teraz tworzyć i edytować filtry rozwiązań (.slnf) bezpośrednio z CLI. Dla monorepozytoriów i dużych rozwiązań w stylu Microsoft, otwieranie 200-projektowego SLN-u do pracy nad trzema z nich było realnym kosztem. Teraz możesz określić zakres z terminala:

dotnet new slnf --name MyApp.slnf
dotnet sln MyApp.slnf add src/Lib/Lib.csproj
dotnet new slnf --name MyApp.slnf
dotnet sln MyApp.slnf add src/Lib/Lib.csproj
SHELL

Aplikacje oparte na plikach (workflow aplikacji .NET run app.cs) w końcu obsługują #:include, co oznacza, że skrypty C# mogą dzielić pomocnicze na oddzielne pliki. W połączeniu z automatycznym uzupełnianiem w edytorze dla dyrektywy w Roslyn, to przemieszcza aplikacje oparte na plikach z "zabawka" do "może być używana do rzeczywistych narzędzi automatyzacji" - niszy, którą przez lata posiadały PowerShell i małe skrypty Pythona.

.NET run -e FOO=BAR pozwala przekazać zmienne środowiskowe w wierszu poleceń bez eksportowania stanu powłoki lub edytowania profili uruchomieniowych. Drobne, ale jesli kiedykolwiek miales otwarte trzy terminale z rozna wartoscia ASPNETCORE_ENVIRONMENT, znasz ten bol.

.NET watch integruje się z hostami aplikacji Aspire, automatycznie uruchamia się po awarii przy następnym zmianie pliku i łagodniej obsługuje Ctrl+C dla WinForms i WPF (stały problem). .NET format akceptuje --framework dla projektów docelowych. .NET test w trybie MTP obsługuje --artifacts-path. I .NET tool exec / dnx nie wymaga już dodatkowej zgody, co było punktem tarcia dla jednorazowych uruchomień narzędzi.

Punkty bólu

Zbalansowana recenzja musi obejmować ostre krawędzie, a Preview 3 je ma.

Historia narzędzi jest surowa. Visual Studio 2026 jest wciąż w wersji zapoznawczej sześć miesięcy po wydaniu .NET 10, a agenci budowlani hostowani przez Microsoft nie mają jeszcze stabilnego wsparcia dla VS 2026. Zmiana w wydaniu poprawki SDK .NET 10 wymagała MSBuild 18 (VS 2026), co jest czystym naruszeniem gwarancji semver promowanych przez Microsoft. Każdy, kto uruchamia CI na agentach hostowanych przez Microsoft, musiał przypiąć SDK 10.0.4 lub przejść na obrazy budowania w wersji zapoznawczej. Jeśli rozważasz przeniesienie CI na zapoznawcze wersje .NET 11, spodziewaj się więcej tego samego - wersje SDK w zapoznaniu, zgodnie z przyznaniem zespołu, większyły problemy w wersjach 10.0.2 i 10.0.3 przed stabilizacją.

Async Runtime nadal jest opcjonalne. Nawet po usunieciu blokady funkcji preview, trzeba przelaczyc runtime-async=on. To w porządku dla kodu na nowym miejscu; dla bibliotek wydawanych w NuGet nie możesz nadal przyjąć, że twoi konsumenci mają przełącznik włączony, więc praktyczne korzyści są odłożone do momentu, gdy funkcja stanie się domyślną (nie w .NET 11).

Wymogi sprzętowe zmieniają się. Minimalne wymagania zestawu instrukcji x86/x64 zostały podniesione. Większość zespołów tego nie zauważy. Niektóre zespoły - i dowiedzą się przy wdrożeniu, jeśli wcześniej tego nie sprawdzą.

Nie LTS, ale STS. .NET 11 jest obsługiwane przez 24 miesiące. .NET 10, obecne LTS, otrzymuje 36 miesięcy. Dla firm z powolnym cyklem aktualizacji, .NET 10 wciąż pozostaje bardziej konserwatywnym wyborem, a przyjęcie .NET 11 oznacza zobowiązanie się do kolejnej aktualizacji w 2028 roku. Argumentem za przyjęciem STS są funkcje; argumentem przeciwko jest kalendarz.

Zapowiedź oznacza zapowiedź. To nie jest narzekanie na stabilność - proces zapoznawczy Microsoftu był dobry - ale Preview 3 nie jest kandydatem do wydania. Produkcje wdrożeniowe czekają co najmniej na RC1. Narzędzia wewnętrzne, projekty boczne i eksploracja to aktualnie właściwy zakres.

Werdykt

Jeśli piszesz C# każdego dnia, warto zainstalować i przetestować .NET 11 Preview 3 w tym tygodniu - szczególnie ze względu na włączenie typów związkowych, które są najważniejszą zmianą językową od lat. Jeśli utrzymujesz biblioteki, praca nad JIT i Async Runtime oznacza, że twój kod będzie szybszy na .NET 11 bez zmian, co jest najlepszym rodzajem aktualizacji. Jeśli wydajesz aplikacje MAUI, prace nad Mapą i gestami to prawdziwy postęp.

Jeśli uruchamiasz produktywne obciążenia .NET, odpowiedź brzmi: planuj dalej, obserwuj dalej i zaznacz GA w listopadzie. Rzeczy, które ekscytują, są udostępniane, ale łańcuch narzędzi - VS, agenci budowlani i tempo poprawek SDK - to miejsce, w którym w rzeczywistości tkwi tarcie i to jeszcze nie zostało rozwiązane.

| --- |

Źródła: .NET Blog announcement, What's new in .NET 11 (Microsoft Learn), Runtime release notes, ASP.NET Core release notes, SDK release notes, Explore union types in C# 15.