Vårt delingshjørne
.NET 10 og C# 14 - Høydepunkter
.NET 10 og C# 14 - Høydepunkter
.NET 10 og C# 14 - Høydepunkter





Steinar
Kollerud
Utvikler
Utvikler
6. november 2025
Utvikling
Med .NET Conf 2025 neste uke nærmer vi oss lanseringen av .NET 10 og C# 14, og det er mye spennende i vente. Etter å ha delt en rekke LinkedIn-poster om de viktigste nyhetene, tenkte jeg å samle alle punktene i en oversiktlig artikkel. Her får du en rask gjennomgang av de mest interessante endringene.
Ny syntax for extensions i C# 14 – en ny måte å organisere extension-metoder
En av de mest aktuelle featurene i C# 14 er uten tvil den nye syntaxen for extension-metoder. Dette er først og fremst en syntaktisk forbedring rettet mot lesbarhet og organisering.
#1 - Egen syntax for extensions
Nå blir det langt enklere å se hva som faktisk er en extension. Man slipper å lete etter "this" i parameter-listen, og det er ye tydeligere hva som foregår.
public static class MyExtensions { // Old extensions syntax public static IEnumerable<Employee> StartDateLaterThan( this IEnumerable<Employee> employees, DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime); // New extensions syntax extension(IEnumerable<Employee> employees) { public IEnumerable<Employee> StartDateLaterThan( DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime
#2 - Naturlig gruppering basert på receiver
Extensions kan grupperes mer logisk ut fra hvilken receiver de hører til. Det gir en mer intuitiv kodebase som er enklere å navigere.
public static class MyExtensions { // Extensions block for all extensions on receiver // IEnumerable<Employee> extension(IEnumerable<Employee> source) { public IEnumerable<Employee> StartDateLaterThan( DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime); public IEnumerable<Employee> IsActive() => employees.Where(e => e.IsActive
#3 - Extension properties og statiske extensions
C# 14 gir oss også støtte for extension properties. Det betyr at man endelig kan bruke property-syntax selv om man ikke har tilgang til den opprinnelige klassen. Et stort pluss for ryddighet og konsistens i koden.
public static class MyExtensions { // Extension properties extension(Employee employee) { public string FullName => $"{employee.FirstName} {employee.LastName}"
Field og null-conditional assignment – kult, men litt blandete følelser
To av nyhetene som jeg er usikker på jeg liker eller hater er "field" for automatisk backing field og null-conditional assignment. Begge retter seg mot å kutte litt kode, men er det utelukkende positivt?
#1 - Field-nøkkelordet
Du slipper å skrive eksplisitte backing fields når du har behov for logikk i property accessors. Smart på papiret, men er det egentlig en god idé å legge mer logikk i get og set?
// Old property accessor body syntax private string _firstName; public string FirstName { get => _firstName; set => _firstName = string.IsNullOrWhiteSpace(value) ? throw new ArgumentException( "Value cannot be null or whitespace.", nameof(value)) : value; } // New property accessor body syntax public string FirstName { get; set => field = string.IsNullOrWhiteSpace(value) ? throw new ArgumentException( "Value cannot be null or whitespace.", nameof(value)) : value
#2 - Null-conditional assignment
I C# 14 kan du skrive person?.Age = 30 uten å sjekke om person er null med en tradisjonell if-statement? Det sparer noen linjer kode, men om det er en forbedring er jeg usikker på.
// Old null-conditional assignment if (employee is not null) { employee.FirstName = "Ola"; employee.LastName = "Normann"; employee.LastUpdated = DateTimeOffset.Now; } // New null-conditional assignment employee?.FirstName = "Ola"; employee?.LastName = "Normann"; employee?.LastUpdated = DateTimeOffset.Now
Disse to endringene er typiske eksempler på syntactic sugar. Det ser stilig ut, men man bør spørre seg om det faktisk forbedrer kodebasen eller kun gjør den mer uleselig.
Testing får et løft – enklere ende til ende tester
En av de mest praktiske forbedringene i .NET 10 finner du på testfronten. Jeg er selv tilhenger av integrasjonstester, og nå blir disse enda kraftigere.Ved å kombinere UseKestrel() og StartServer() med WebApplicationFactory<TEntryPoint> kan man starte applikasjonen som en ekte server, direkte fra testene. Det betyr at du kan kjøre fullverdige ende til ende tester der frontend og backend snakker sammen i samme testløp, uten å hoste backend separat.
// InitializeAsync() and DisposeAsync() of a class implementing // WebApplicationFactory<TEntryPoint> public async Task InitializeAsync() { await _sqlContainer.StartAsync(); // use overload UseKestrel(int port) to specify port UseKestrel(); StartServer(); HttpClient = CreateClient(); } public new Task DisposeAsync() { return _sqlContainer.StopAsync
Dette kommer på toppen av det vi allerede fikk med Testcontainers, og åpner for et langt mer helhetlig og realistisk testmiljø.
Filbaserte apper – .NET uten prosjektfil
Microsoft ønsker å senke terskelen for å komme i gang med .NET, og filbaserte apper er et godt eksempel på det.
#1 - Kjør .NET med én enkelt .cs-fil
Nå trenger du ingen .csproj eller prosjektstruktur for å skrive små apper. Du kan kjøre en enkelt fil med dotnet run app.cs.
dotnet run app.cs
// app.cs Console.WriteLine("Hello .NET 10 from C# script!"
#2 - Innebygd støtte for pakker og SDK
Du kan legge til NuGet-pakker og angi SDK direkte i filen med #:package og #:sdk.
// app.cs package System.Text.Json; // #r:sdk <SDK name> // #r:property using System.Text.Json; var p = new Person("Ola", 42); Console.WriteLine(JsonSerializer.Serialize(p)); Console.WriteLine($"Hello .NET 10 from C# script!"); record Person(string Name, int Age
#3 - Konvertering til fullt prosjekt
Skulle appen vokse kan du konvertere den til et vanlig prosjekt med én kommando. Praktisk og fleksibelt.
dotnet project convert app.cs
Dette kommer kanskje ikke til å endre hverdagen for erfarne .NET-utviklere, men er enda en feature som Microsoft håper skal gjøre plattformen mer tilgjengelig for nybegynnere og de som kommer fra mer skriptorienterte språk.
LINQ og EF Core – Nye typer og joins
I .NET 10 får både LINQ og Entity Framework Core flere etterlengtede utvidelser.
#1 - LeftJoin og RightJoin i LINQ
Endelig kan du skrive LeftJoin eller RightJoin direkte, uten å ty til workarounds med GroupJoin og SelectMany. Mye renere, mye lettere å lese.
// Left join before .NET 10 :( var q = ctx.Students .GroupJoin( ctx.Departments, s => s.DeptId, d => d.Id, (s, deps) => new { s, deps }) .SelectMany( x => x.deps.DefaultIfEmpty(), (x, d) => new { x.s.Name, Dept = d is not null ? d.Name : "[NONE]"
// Left join in .NET 10 :) var q = ctx.Students .LeftJoin( ctx.Departments, s => s.DeptId, d => d.Id, (s, d) => new { s.Name, Dept = d is not null ? d.Name : "[NONE]"
#2 - JSON-kolonner i EF Core
Du slipper å lagre JSON som tekst og gjøre manuell serialisering. EF Core 10 gir innebygd støtte for JSON-kolonner i SQL Server og Azure SQL.
// Old json columns public class Document { public int Id { get; set; } // Stored as nvarchar(max) public string MetadataJson { get; set; } = "{}"; } // Query on raw JSON string var docs = await ctx.Documents .Where(d => d.MetadataJson.Contains("\"lang\":\"en\"")) .ToListAsync
using System.Text.Json.Nodes; // New support for json columns public class Document { public int Id { get; set; } [Column(TypeName = "json")] public JsonObject Metadata { get; set; } = new() { ["lang"] = "en", ["tags"] = new JsonArray("search", "ai"
#3 - Komplekse typer som JSON
Du kan mappe verdier som objekter direkte til én kolonne og filtrere på innholdet i JSON uten rå SQL. En funksjon som virkelig gjør EF Core mer moderne og fleksibel.
public class Customer { public int Id { get; set; } public ContactInfo Contact { get; set; } = new(); } [ComplexType] public class ContactInfo { public string Email { get; set; } = ""; public Address Address { get; set; } = new(); } public class Address { public string Line1 { get; set; } = ""; public string City { get; set; } = ""; public string Country { get; set; } = ""
public class AppDbContext : DbContext { public DbSet<Customer> Customers => Set<Customer>(); protected override void OnModelCreating( ModelBuilder modelBuilder) { var c = modelBuilder.Entity<Customer>(); // Store Contact as one JSON column c.ComplexProperty(x => x.Contact).ToJson(); c.Property<int>("Id"); } }
// Find customers in a city // EF translates into JSON path in SQL var oslo = await ctx.Customers .Where(c => c.Contact.Address.City == "Oslo") .ToListAsync(); // Bulk update a nested JSON property (no manual SQL) await ctx.Customers .Where(c => c.Contact.Email.EndsWith("@contoso.com")) .ExecuteUpdate(s => s.SetProperty( c => c.Contact.Address.Country, "NO"
Oppsummert
Alt i alt føles releasen av .NET 10 og C# 14 litt tynnere enn forventet. Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på. Dette er etter min mening de mest relevante og interessante funksjonene, men hva synes du? Er det nok nytt til å skape entusiasme, eller burde det kommet enda mer?
Med .NET Conf 2025 neste uke nærmer vi oss lanseringen av .NET 10 og C# 14, og det er mye spennende i vente. Etter å ha delt en rekke LinkedIn-poster om de viktigste nyhetene, tenkte jeg å samle alle punktene i en oversiktlig artikkel. Her får du en rask gjennomgang av de mest interessante endringene.
Ny syntax for extensions i C# 14 – en ny måte å organisere extension-metoder
En av de mest aktuelle featurene i C# 14 er uten tvil den nye syntaxen for extension-metoder. Dette er først og fremst en syntaktisk forbedring rettet mot lesbarhet og organisering.
#1 - Egen syntax for extensions
Nå blir det langt enklere å se hva som faktisk er en extension. Man slipper å lete etter "this" i parameter-listen, og det er ye tydeligere hva som foregår.
public static class MyExtensions { // Old extensions syntax public static IEnumerable<Employee> StartDateLaterThan( this IEnumerable<Employee> employees, DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime); // New extensions syntax extension(IEnumerable<Employee> employees) { public IEnumerable<Employee> StartDateLaterThan( DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime
#2 - Naturlig gruppering basert på receiver
Extensions kan grupperes mer logisk ut fra hvilken receiver de hører til. Det gir en mer intuitiv kodebase som er enklere å navigere.
public static class MyExtensions { // Extensions block for all extensions on receiver // IEnumerable<Employee> extension(IEnumerable<Employee> source) { public IEnumerable<Employee> StartDateLaterThan( DateTimeOffset dateTime) => employees.Where(e => e.StartDate > dateTime); public IEnumerable<Employee> IsActive() => employees.Where(e => e.IsActive
#3 - Extension properties og statiske extensions
C# 14 gir oss også støtte for extension properties. Det betyr at man endelig kan bruke property-syntax selv om man ikke har tilgang til den opprinnelige klassen. Et stort pluss for ryddighet og konsistens i koden.
public static class MyExtensions { // Extension properties extension(Employee employee) { public string FullName => $"{employee.FirstName} {employee.LastName}"
Field og null-conditional assignment – kult, men litt blandete følelser
To av nyhetene som jeg er usikker på jeg liker eller hater er "field" for automatisk backing field og null-conditional assignment. Begge retter seg mot å kutte litt kode, men er det utelukkende positivt?
#1 - Field-nøkkelordet
Du slipper å skrive eksplisitte backing fields når du har behov for logikk i property accessors. Smart på papiret, men er det egentlig en god idé å legge mer logikk i get og set?
// Old property accessor body syntax private string _firstName; public string FirstName { get => _firstName; set => _firstName = string.IsNullOrWhiteSpace(value) ? throw new ArgumentException( "Value cannot be null or whitespace.", nameof(value)) : value; } // New property accessor body syntax public string FirstName { get; set => field = string.IsNullOrWhiteSpace(value) ? throw new ArgumentException( "Value cannot be null or whitespace.", nameof(value)) : value
#2 - Null-conditional assignment
I C# 14 kan du skrive person?.Age = 30 uten å sjekke om person er null med en tradisjonell if-statement? Det sparer noen linjer kode, men om det er en forbedring er jeg usikker på.
// Old null-conditional assignment if (employee is not null) { employee.FirstName = "Ola"; employee.LastName = "Normann"; employee.LastUpdated = DateTimeOffset.Now; } // New null-conditional assignment employee?.FirstName = "Ola"; employee?.LastName = "Normann"; employee?.LastUpdated = DateTimeOffset.Now
Disse to endringene er typiske eksempler på syntactic sugar. Det ser stilig ut, men man bør spørre seg om det faktisk forbedrer kodebasen eller kun gjør den mer uleselig.
Testing får et løft – enklere ende til ende tester
En av de mest praktiske forbedringene i .NET 10 finner du på testfronten. Jeg er selv tilhenger av integrasjonstester, og nå blir disse enda kraftigere.Ved å kombinere UseKestrel() og StartServer() med WebApplicationFactory<TEntryPoint> kan man starte applikasjonen som en ekte server, direkte fra testene. Det betyr at du kan kjøre fullverdige ende til ende tester der frontend og backend snakker sammen i samme testløp, uten å hoste backend separat.
// InitializeAsync() and DisposeAsync() of a class implementing // WebApplicationFactory<TEntryPoint> public async Task InitializeAsync() { await _sqlContainer.StartAsync(); // use overload UseKestrel(int port) to specify port UseKestrel(); StartServer(); HttpClient = CreateClient(); } public new Task DisposeAsync() { return _sqlContainer.StopAsync
Dette kommer på toppen av det vi allerede fikk med Testcontainers, og åpner for et langt mer helhetlig og realistisk testmiljø.
Filbaserte apper – .NET uten prosjektfil
Microsoft ønsker å senke terskelen for å komme i gang med .NET, og filbaserte apper er et godt eksempel på det.
#1 - Kjør .NET med én enkelt .cs-fil
Nå trenger du ingen .csproj eller prosjektstruktur for å skrive små apper. Du kan kjøre en enkelt fil med dotnet run app.cs.
dotnet run app.cs
// app.cs Console.WriteLine("Hello .NET 10 from C# script!"
#2 - Innebygd støtte for pakker og SDK
Du kan legge til NuGet-pakker og angi SDK direkte i filen med #:package og #:sdk.
// app.cs package System.Text.Json; // #r:sdk <SDK name> // #r:property using System.Text.Json; var p = new Person("Ola", 42); Console.WriteLine(JsonSerializer.Serialize(p)); Console.WriteLine($"Hello .NET 10 from C# script!"); record Person(string Name, int Age
#3 - Konvertering til fullt prosjekt
Skulle appen vokse kan du konvertere den til et vanlig prosjekt med én kommando. Praktisk og fleksibelt.
dotnet project convert app.cs
Dette kommer kanskje ikke til å endre hverdagen for erfarne .NET-utviklere, men er enda en feature som Microsoft håper skal gjøre plattformen mer tilgjengelig for nybegynnere og de som kommer fra mer skriptorienterte språk.
LINQ og EF Core – Nye typer og joins
I .NET 10 får både LINQ og Entity Framework Core flere etterlengtede utvidelser.
#1 - LeftJoin og RightJoin i LINQ
Endelig kan du skrive LeftJoin eller RightJoin direkte, uten å ty til workarounds med GroupJoin og SelectMany. Mye renere, mye lettere å lese.
// Left join before .NET 10 :( var q = ctx.Students .GroupJoin( ctx.Departments, s => s.DeptId, d => d.Id, (s, deps) => new { s, deps }) .SelectMany( x => x.deps.DefaultIfEmpty(), (x, d) => new { x.s.Name, Dept = d is not null ? d.Name : "[NONE]"
// Left join in .NET 10 :) var q = ctx.Students .LeftJoin( ctx.Departments, s => s.DeptId, d => d.Id, (s, d) => new { s.Name, Dept = d is not null ? d.Name : "[NONE]"
#2 - JSON-kolonner i EF Core
Du slipper å lagre JSON som tekst og gjøre manuell serialisering. EF Core 10 gir innebygd støtte for JSON-kolonner i SQL Server og Azure SQL.
// Old json columns public class Document { public int Id { get; set; } // Stored as nvarchar(max) public string MetadataJson { get; set; } = "{}"; } // Query on raw JSON string var docs = await ctx.Documents .Where(d => d.MetadataJson.Contains("\"lang\":\"en\"")) .ToListAsync
using System.Text.Json.Nodes; // New support for json columns public class Document { public int Id { get; set; } [Column(TypeName = "json")] public JsonObject Metadata { get; set; } = new() { ["lang"] = "en", ["tags"] = new JsonArray("search", "ai"
#3 - Komplekse typer som JSON
Du kan mappe verdier som objekter direkte til én kolonne og filtrere på innholdet i JSON uten rå SQL. En funksjon som virkelig gjør EF Core mer moderne og fleksibel.
public class Customer { public int Id { get; set; } public ContactInfo Contact { get; set; } = new(); } [ComplexType] public class ContactInfo { public string Email { get; set; } = ""; public Address Address { get; set; } = new(); } public class Address { public string Line1 { get; set; } = ""; public string City { get; set; } = ""; public string Country { get; set; } = ""
public class AppDbContext : DbContext { public DbSet<Customer> Customers => Set<Customer>(); protected override void OnModelCreating( ModelBuilder modelBuilder) { var c = modelBuilder.Entity<Customer>(); // Store Contact as one JSON column c.ComplexProperty(x => x.Contact).ToJson(); c.Property<int>("Id"); } }
// Find customers in a city // EF translates into JSON path in SQL var oslo = await ctx.Customers .Where(c => c.Contact.Address.City == "Oslo") .ToListAsync(); // Bulk update a nested JSON property (no manual SQL) await ctx.Customers .Where(c => c.Contact.Email.EndsWith("@contoso.com")) .ExecuteUpdate(s => s.SetProperty( c => c.Contact.Address.Country, "NO"
Oppsummert
Alt i alt føles releasen av .NET 10 og C# 14 litt tynnere enn forventet. Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på. Dette er etter min mening de mest relevante og interessante funksjonene, men hva synes du? Er det nok nytt til å skape entusiasme, eller burde det kommet enda mer?
Kanskje du liker:
Kanskje du liker:
Kanskje du liker:
Utvikling
6. november 2025
Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.
Utvikling
6. november 2025
Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.
Utvikling
4. august 2025
Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.
Utvikling
4. august 2025
Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.
Utvikling
5. juni 2025
Dette var personlig min aller første konferanse. For å være helt ærlig visste jeg ikke helt hva jeg kunne forvente. Er det som en bedriftspresentasjon der JetBrains er bedriften? Hvorfor drar man egentlig på konferanse?
Utvikling
5. juni 2025
Dette var personlig min aller første konferanse. For å være helt ærlig visste jeg ikke helt hva jeg kunne forvente. Er det som en bedriftspresentasjon der JetBrains er bedriften? Hvorfor drar man egentlig på konferanse?
Utvikling
6. november 2025
Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.
Utvikling
4. august 2025
Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.
Utvikling
6. november 2025
Det er ikke mange banebrytende endringer, men de få nyhetene som faktisk kommer har likevel potensial til å forbedre måten vi skriver, organiserer og tester kode på.
Utvikling
4. august 2025
Denne artikkelen av Adrian Brenne viser steg-for-steg hvordan du bruker både system- og bruker-tilordnet Managed Identity for å sikre tilgang til tjenester som Azure Storage og Key Vault med minimal risiko og maksimal effektivitet.
Copyright ©2025. All rights reserved.
Copyright ©2025. All rights reserved.
Copyright ©2025. All rights reserved.
Copyright ©2025. All rights reserved.


