C# 14

Nella .NET conf 2025 è stato presentata la nuova versione di C#, in questo articolo vedremo quali sono le novità e come ci possono tornare utili nel quotidiano.

Per aiutarvi ho creato anche un repository Github con alcuni esempi di codice, potete trovarlo qui:

Extension members

Quando parliamo di extension members parliamo della possibilità di estendere una classe con nuove funzionalità (classi o metodi).

Property nelle estensioni

Una delle novità rilasciate, discusse nella versione 13 del linguaggio, è quella di poter aggiungere delle proprietà (extension properties) oltre ai metodi (extension methods)

Ipotizziamo di voler aggiungere una property all’interfaccia IEnumberable. Prima di tutto creiamo una classe statica (magari in una libreria dedicata per massimizzare il riutilizzo) e poi andiamo ad inserire la nuova proprietà

C#
public static class MyEnumerableExtensions {
  extension<TSource>(IEnumerable<TSource> source) {
    //Extension property
    public bool ThereAreNoElements => !source.Any();
  }
}


IEnumerable<int> myList = new List<int> { 1, 2, 3 };
myList.ThereAreNoElements; // --> False

Estensione dei tipi

Nell’esempio precedente la property ThereAreNoElements era disponibile su una classe istanziata, è però possibile creare dei metodi e delle property di estensione anche sui tipi. Riprendendo l’esempio di IEnumerable aggiungiamo due elementi: un esempio ed una funzione per fare il merge e l’estensione dell’operatore +.

C#
public static class MyEnumerableExtensions {
  extension<TSource>(IEnumerable<TSource>) {
  
  // Static extension method
  public static IEnumerable<TSource> MyCustomMerge(IEnumerable<TSource> first, IEnumerable<TSource> second){
      //...
    }
  }
  
  //Static extension of operator +
  public static IEnumerable<TSource> operator +(IEnumerable<TSource> first, IEnumerable<TSource> second) {
    //...
  }
}


IEnumerable<int> myList1 = new List<int> { 1, 2, 3 };
IEnumerable<int> myList2 = new List<int> { 4, 5, 6 };

IEnumerable<int>.MyCustomMerge(myList1,myList2) // --> 1,2,3,4,5,6
myList1 + myList2  // --> 1,2,3,4,5,6

Nuova parola chiave field

Questa nuova feature è davvero interessante e penso di averla desiderata molto spesso. Vi ricordate quando bisognava creare una property pubblica ed una privata per andare a gestire il set del valore? Ecco, ho volutamente parlato al passato perchè ora non sarà più necessario scrivere una property privata, in quanto la nuova parola chiave field verrà risolta a livello di compilatore, permettendoci di avere lo stesso risultato di prima ma con meno codice scritto.

C#
// Before C# 14 ☹️
private int _serialNumber;
public int  SerialNumber
{
    get => _serialNumber;
    set => _serialNumber = value ?? throw new ArgumentNullException("Serial number is required");
}


//Now with C# 14 😍
public int  SerialNumber
{
    get;
    set => field = value ?? throw new ArgumentNullException("Serial number is required");
}

Assegnazioni con valore null

Ipotizziamo di avere una classe con valore null. Cosa dovrei fare per accedere ad una proprietà dell’oggetto per assegnarli un valore? Ad oggi devo necessariamente verificare che l’oggetto sia istanziato prima di procedere con l’assegnazione (sennò si genererebbe un’exception).

In C#14 l’assegnazione su valori null sarà gestito direttamente dal compilatore e non verrà generata nessuna exception (ovviamente dovremo utilizzare l’operatore condizionale di null)

C#
// Before C# 14
if (myNewLightSaber is not null)
{
    myNewLightSaber.mainCristal = new KyberCristal(123213, "Green");
}

// With C# 14
myNewLightSaber?.mainCristal = new KyberCristal(123213, "Green");

Conversione implicita dei tipi Span

In questo articolo avevo parlato di Span e ReadonlySpan che possiamo definire come delle “viste” sui dati di altri oggetti, caratteristica che li rende molto performanti in alcuni contesti (se vuoi approfondire leggi l’articolo dedicato).

In C# 14 avverrà una conversione implicita da un nostro oggetto verso Span, come nell’esempio qui sotto:

C#
public void PrintArray(Span<int> list)
{
    ...
}

public void PrintArrayAgain(ReadonlySpan<int> list)
{
    ...
}

//Automatic cast from int[] to Span<int>/ReadonlySpan<int>
int[] myArray = { 1, 2, 3, 4, 5 };
PrintArray(myArray); // --> "1,2,3,4,5"
PrintArrayAgain(myArray); // --> "1,2,3,4,5"

Argomenti anonimi nelle espressioni lambda

Prima di C# 14 era obbligatorio definire il tipo nelle espressioni lambda, ora il tipo è dedotto in funzione del contenuto dell’espressione: in caso di violazione il codice non compilerà.

C#
delegate bool TryParse<T>(string text, out T result);

// Before C# 14
TryParse<int> parseOld = (string text, out int result) => Int32.TryParse(text, out result);

// With C#14
TryParse<int> parseNew = (       text, out result) => Int32.TryParse(text, out result);

int parsedValue = 0;
parseNew("2", out parsedValue); // It works!
parseNew( 2 , out parsedValue); // Doesn't compile!

Nameof

L’espressione nameof ora è in grado di ricevere come input anche un tipo generico non associato, per esempio List<>

C#
// Before 
nameof(List<int>) // --> List
nameof(Dictionary<string, int>) // --> Dictionary

//With C# 14
nameof(List<>) // --> List
nameof(Dictionary<,>) // --> Dictionary

Conclusioni

In questo articolo abbiamo visto le più recenti novità in relazione alla nuova versione di C#, presentato nella .NET conf 2025.

Se siete curiosi di scoprire altre novità in merito a .NET 10 vi consiglio di leggere gli altri articoli dedicati su questo blog!

Alla prossima 😉

Condividi questo articolo
Shareable URL
Post precedente

Claude 4.5

Prosimo post

ASP.NET Core 10 – Le novità!

Leggi il prossimo articolo