Ci siamo quasi a fine settembre, nella .Net conf del 23-25 settembre 2019 verrà rilasciata in GA la versione finale di .net core 3.

Per poter vedere prima degli altri .net core 3 e cominciare ad apprezzare i cambiamenti potete scaricare i pacchetti che servono dalla pagina ufficiale di .net core oppure più semplicemente passare al canale preview di Visual studiose siete utenti mac o linux di Visual Studio probabilmente sapete già come passare al canale preview senza bisogno di maggiori informazioni. Al momento in cui scrivo (metà settembre 2019) è stata rilasciata la rc1 di .net core 3, come sempre non ci sono garanzie sul fatto che tutte le caratteristiche presenti nella preview siano poi effettivamente portate in GA ma data la vicinanza del rilascio sono ragionevolmente confidente.

Vi segnalo inoltre il modulo dot net try per .net a riga di comando che apre una pagina web in cui scrivere codice in C#, utile sia per testare le novità del linguaggio sia per fare dei piccoli test senza creare decine di progetti inutili sulle nostre macchine.

Le novità, al livello di linguaggio che saranno introdotte dalla .Net conf del 23-25 settembre 2019 saranno principalmente su due fronti:

  • Novità di C# versione 8
  • .Net Core Versione 3

Come è ovvio le novità di .net core 3 riguarderanno “solo” l’universo creato da .net core mentre le novità introdotte nel linguaggio si rifletteranno anche (a richiesta) sul “vecchio” framework .net.

Novità di C# 8

Potete trovare tutte le novità su Novità di C# 8 e sulla relativa repository github.

Al momento in cui scrivo è stata rilasciata la RC1 di C# 8

Tutto il codice presentato in questo articolo è presente anche sulla repository Github https://github.com/DotNetCodeIT/Meetups

Membri in sola lettura

E’ stato introdotto il modificatore readonly su qualunque membro di una struct. All’interno di un metodo marcato come readonly non è possibile modificare il contenuto di un field perchè genererebbe un errore di compilazione. Anche il richiamare un metodo non readonly genera un warning. Questo rende più difensivo il nostro codice anche se, purtroppo, questa caratteristica è limitata alle sole struct e non alle classi.

Modifiche alle interfacce

Sono state introdotte varie modifiche alle interfacce in C# 8 in particolare sono stati introdotti i metodi di default nell’interfaccia e, quasi come conseguenza naturale, la possibilità di inserire metodi statici nelle interfacce.

A me fa parecchio strano scrivere definizione di metodi nell’interfaccia

Membri di interfacce predefiniti

E’ Possibile “definire nell’interfaccia” dei metodi di default. Questo rende le interfacce un pochino più simili alle classi astratte.

Questa nuova funzionalità è molto utile se si vuole garantire la retro compatibilità con le versioni precedenti di una certa api.

Ipotiziamo di aver definito due classi concrete DefaultMember (che non ridefinisce DefaultMethod) e FullImplementation (che al contrario lo ridefinisce)

Membri statici di interfaccie

È possibile definire dei membri statici di interfacce che varranno per tutte le classi derivate.

Ipotizzando di avere due classi concrete in cui non è stata ridefinita PrintHello()

Combinando i metodi statici definiti nelle interfacce e le implementazioni di default dei metodi è possibile Estendere l’implementazione predefinita

Più pattern per l’operatore switch

Questa feature è la mia preferita perché presente anche in altri linguaggi moderni come swift.

La documentazione recita questa feature rappresenta il primo tentativo andare verso un paradigma che divida dati e funzionalità e noi non possiamo che esserne felici.

Switch expression

Le espressioni switch sono il naturale proseguimento dei metodi “senza corpo” o meglio delle Expression body definition.

Questa caratteristica rende notevolmente più snello e leggibile il codice di alcuni tipi di switch.

notare l’inserimento di _ al posto di default

Le switch expression risultano, come è ovvio particolarmente comode in tutte quelle situazioni in cui è necessario mappare dei valori.

Property patterns

Il property pattern è di gran lunga la feature nuova di C# 8 che preferisco perchè fin da quando è uscita su swift la ho sempre invidiata parecchio.

in sostanza questa nuova caratteristica consente (qui scritta in forma compatta) consente di usare una classe in un istruzione di switch e di utilizzare come filtro contemporaneamente più field della classe.

L’esempio qui sotto è di facile interpretazione: dato un oggetti City che contiene sia il nome di uno stato che il nome di una città la funzione qui sotto estrare una label. Questo esempio, di scarso significato pratico, serve solo a dimostrare quanto sia facile con C# 8 fare pattern matching su oggetti complessi senza far ricorso a if nei rami dello switch.

Con il Property pattern è possibile usare la forma estesa, e più familiare, dello switch; lo trovate nella repository di gitbhub allegata

Tuple pattern

Di significato molto simile al property pattern è possibile usare le Tuple come argomento dello switch

Positional Pattern

E’ stato introdotto il Decostruttore per alcuni tipi. Il Decostruttore di suo non è molto utile perchè “appiattisce” un oggetto in una lista di parametri.

Di suo non è un costrutto molto utile, si puo’ già fare con le vecchie versioni di C# usando una nomenclatura “non standard”. Accoppiato con la clausula when degli switch invece risulta particolarmente efficace.

In questo esempio possiamo vedere quanto sia facile mappare un punto in un piano cartesiano per capire su quale quadrante è posizionato. Il pointModel viene Decostruito ed iserito automaticamente in una tupla, poi viene usata la clausula when.

La clausula when più che l’estenzione dello switch possiamo vederlo come un modo per rendere più elegante else if

Potete vedere esempi più avanzatati di pattern matching nell’Esercitazione: Uso di funzionalità di criteri di ricerca per estendere i tipi di dati

Using declaration e struct IDisposable

Con C# 8 le definizioni di using risultano notevolmente semplificate (sopratutto nella lettura)

Notare il punto e virgola alla fine dello using e che in questa versione lo using non usa le parentesi. La disposableClass viene rilasciata alla fine del metodo.

Dal punto di vista pratico se la variabile dichiarada nello using puo’ vivere fino alla fine del metodo che lo contiene allora non è più necessario inserire il blocco di codice sotto lo using.

Ulteriore modifica è la possibilità di dichiarare una struttura IDisposable

Funzioni locali statiche

Le funzioni locali statiche sono un’estenzione delle funzioni locali già presenti nel linguaggio

Flussi asincroni

Con C# 8 è stato introdotto il supporto ai flussi asinctroni. In sintesi i flussi asinctroni sono metodi async che tornano un oggetto di tipo IAsyncEnumerable<T> e che hanno un return espesso con uno yeld.

Un esempio semplice di flusso asincrono è:

questo flusso asincrono puo’ essere consumato a partire da un nuovo costrutto await foreach che è molto leggibile rispetto a soluzioni più barocche.

L’utilizzo di await foreach è stato creato per consumare i flussi asincroni ed è una soluzione particolarmente efficiente nel caso in cui un Parallel.foreach non sia sufficiente in quanto è necessario garantire l’ordine in cui saranno letti gli elementi della lista.

Dal punto di vista pratico se si dichiara una funzione statica locale allora le variabili dichiarate al di fuori del corpo della funzione statica allora queste variabili risultano inaccessibili.

Indici ed intervalli

Sono stati introdotti gli oggetti System.Index e System.Range

Indici

In sostanza usando gli indici ora è possibile accedere direttamente agli ultimi elementi di una collezione senza dover necessariamente fare delle conversioni con Lenght – x.

Semplificando, per accedere agli elementi di una lista in ordine cresciente si possono usare gli indici “normalmente” andando da 0 a Lenght -1.

Sono stati introdotti anche gli indici “inversi” che lavorano sugli ultimi elementi che vanno da ^1 a ^Lenght.

  • ^1 viene interpretato come Lenght -1
  • ^2 viene interpretato come Lenght -2
  • ^Lenght viene interpretato come Lenght- Lenght quindi 0
  • ^0 viene interprepato come Lenght – 0 quindi Lenght e quindi genera un’eccezione

nello specifico avendo la sequente lista

Range

E’ stato introdotto l’operatore (per le liste) “..” che significa “prendi tutti gli indici nell’intervallo.

1..4 significa prendi tutti gli elementi della lista da 1 a 4 (escludi lo zero e tutto quello che viene dopo il 4)

è possibile usare questo valore senza il lato sinistro o senza il destro.

quindi:

  • [6..] significa prendi gli elementi dal 6 in poi
  • [..6] significa prendi tutti gli eleneti fino al 6

Tipi di riferimento nullable

Questa caratteristica deve essere esplicitamente attivata separatamente a partire dal file di progetto

in base alla vostra versione di visual studio il tag Nullable potrebbe essere modificato in NullableReferenceTypes.

In alternativa è possibile attivare la funzionalita in un singolo blocco di codice

  • #nullable enable: imposta il contesto dell’annotazione nullable e il contesto dell’avviso nullable su enabled.
  • #nullable disable: imposta il contesto dell’annotazione nullable e il contesto dell’avviso nullable su disabled.
  • #nullable safeonly: impostare il contesto dell’annotazione nullable su enabled e il contesto dell’avviso su safeonly.
  • nullable restore: ripristina le impostazioni di progetto per il contesto dell’annotazione nullable e il contesto dell’avviso nullable.
  • #pragma warning disable nullable: impostare il contesto dell’avviso nullable su disabled.
  • #pragma warning enable nullable: impostare il contesto dell’avviso nullable su enabled.
  • #pragma warning restore nullable: ripristina le impostazioni di progetto per il contesto dell’avviso nullable.
  • #pragma warning safeonly nullable: imposta il contesto dell’avviso nullable su safeonly.

In sostanza se la caratteristica viene attivata gli oggetti non dichiarati esplicitamente come Nullable, se settati a null, genereranno un warning in compilazione.