C# 7 le novità
C# dalla sua nascita è stato sempre qualcosa in più che un puro programma orientato agli oggetti, infatti è stato da subito considerato come
un programma basato sui componenti supportando proprietà, eventi, annotazioni. Da allora il suo sviluppo è stato continuo
nel corso del tempo e dopo le novità introdotte da C#6 come, le direttive using static, expression body members e i null conditional operator
eccoci qui con la nuova versione di C#, la 7, che viene resa disponibile con il rilascio di Visual Studio "17", insieme a molte altre nuove funzionalità, che migliorano le performance di esecuzione, l’accesso ai dati e la semplificazione del codice.
Qui di seguito è riportato l'elenco delle caratteristiche che è mia intenzione approfondire in questo articolo.
Aggiornamenti per versioni di C# 7.x
Features più significative C#7.0:
- Funzioni locali, metodi dichiarati in altri metodi.
- Separatori di cifre e valori letterali per numeri binari.
- Metodi asincroni, restituire tipi generalizzati,
ValueTask
. - Riferimenti locali e valori restituiti per riferimento.
Features più significative di C#7.1 supportato dall'aggiornamento 15.3 di VS2017.
Features più significative C#7.2 supportata dall'aggiornamento 15.5 di VS2017.
Features più significative C#7.3 supportata dall'aggiornamento 15.7 di VS2017.
- Ref Local Reassignment.
- Additional Generic Constraints.
- Attributes Targeting Fields of Auto-Implemented Properties
- Expression Variables in Initializers.
- Equality Operators for Value Tuples.
Anteprima C# 8.0.
Funzioni locali, metodi dichiarati in altri metodi.
Con C# 7 è ora possibile creare funzioni locali, funzioni all’interno di altre funzioni, dette "nested functions" o "local function". La sintassi per dichiarare le funzioni locali è la stessa che si utilizza per dichiarare normalmente un metodo. Per capire come viene implementata una local function, sotto vi è un esempio.
using static System.Console ;
namespace FunzioniLocali
{
class Program
{
static void Main(string [] args)
{
void Addizione(int x, int y) //Prima funzione innestata
{
WriteLine($"Somma {x} e {y} la loro somma è : {x + y}" ); //stampa a video
}
void Moltiplicazione(int x, int y)//Seconda funzione innestata
{
WriteLine($"Moltiplica {x} per {y} ed è uguale a : {x * y}" ); //stampa a video
Addizione(30, 10); // Richiamo la funzione Addizione dentro la funzione Moltiplicazione
}
Addizione(20, 50);
Moltiplicazione(20, 50);
WriteLine("Premi un tasto per continuare..." );
ReadLine();
}
}
}
Nell' esempio sopra ci sono due funzioni innestate "Somma" e "Moltiplicazione" possono essere invocate in qualsiasi punto all'interno di una funzione apparatenete allo stesso blocco di codice e come si può vedere le funzioni locali, se paragonate con i delegati, non solo hanno una sintassi più semplice, ma necessitano di meno risorse.
Separatori di cifre e valori letterali per numeri binari.
Il volore letterale di un numero esadecimale non sempre è di facile lettura :
ulong num = 0xfedcab9876543210;
C#7 introduce il carattere separatore digitale _
(underscore) per ottenere una migliore leggibilità.
Il separatore digitale può essere usato ogni 4 cifre per una migliore leggibilità:
ulong num =0xfedc_ab98_7654_3210;
può essere usato più di un separatore e in ogni posizione ma, mai comunque come prefisso:
ulong num = 0xfedc____ab98____76___54_32____10;
Con C#7 diventa facile definire i valori binari utilizzando il prefisso 0B
oppure 0b
e i separatori ne migliorano la leggibilità.
ushort binary =0b0000_1101_0101_1011;
Metodi asincroni, restituire tipi generalizzati.
In C#7 è stata introdotta una nuova funzionalità che permette ai metodi asincroni di restituire
oltre ai tipi di riferimento già presenti Task, Task<T>
e al tipo void
(questo però sconsigliato)
il nuovo tipo ValueTask
che rispetto a Task
che è una classe quindi tipo di riferimento,
è una struttura quindi è tipo di valore. La restituzione di un oggetto con Task()
può creare colli di bottiglia, per esempio vogliamo ritornare un tipo di valore da un certo metodo, conTask()
sarà creato un nuovo oggetto nell'heap,
se invece uso ValueTask
, non sarà creato nessun oggetto nell'heap in quanto tipo di valore e si potrà usare a questo punto la keyword
await
nel metodo di ritorno, tutto questo può dare un significativo incremento prestazionale.
Ricorda che è assolutamente necessario aggiungere il pacchetto NuGet dotnet add package System.Threading.Tasks.Extensions
per
potere usare il ValueTask<T>
Riferimenti locali e valori restituiti per riferimento.
C#7 introduce la possibilità di usare i ref
con le variabili locali e con i valori di ritorno dei metodi.
Ricordo che il ref
può essere utilizzato con i parametri per passare i riferimenti a questi così se il metodo li modifica manterranno questi valori anche al di fuori dello scope del metodo stesso.
Per chiarire vediamo prima un metodo senza il ref
, nell'esempio il metodo accetta in ingresso un vettore di elemeni e un indice,
restituisce x
, il valore dell'elemento del vettore che ha quell'indice. Nel metodo Ref_local_ref_return
ho un vettore di dati e un indice che vengono passati al metodo,GetArrayDiElementi
,
visto sopra, il valore di ritorno viene salvato nella variabile a1
e successivamente modificato,a1=90
per cui il valore dell'elemento dell'array, differisce da quello della variabile dati[4]==10
.
using static System.Console ;
namespace Riferimenti
{
class Program
{
static void Main()
{
Ref_local_ref_return();
}
private static int GetArrayDiElementi(int [] elem, int indice)
{
int x=elem[indice];
retun x;
}
private static void Ref_local_ref_return()
{
int [] dati={2,4,6,8,10};
int a1=GetArrayDiElementi(dati,4);
WriteLine(nameof (Ref_local_ref_return));
WriteLine($" a1= {a1} " );
a1=90;
WriteLine($" a1= {a1} , dati[4]= {dati[4]}" );
WriteLine("Premi un tasto per continuare..." );
ReadLine();
}
}
}
Creo adesso un altro metodo GetArryDiElementi2
che ha come tipo di ritorno un ref int
e la variabile x
è adesso dichiarata con un ref
diventa una variabilelocal ref
using static System.Console ;
namespace Riferimenti
{
class Program
{
static void Main()
{
Ref_local_ref_return();
}
private static ref int GetArrayDiElementi2(int [] elem, int indice)
{
ref int x=elem[indice];
retun ref x;
}
private static void Ref_local_ref_return()
{
int [] dati={2,4,6,8,10};
ref int a_ref=refGetArrayDiElementi(dati,4);
WriteLine(nameof (Ref_local_ref_return));
WriteLine($" a_ref= {a_ref} " );
a_ref=90;
WriteLine($" a_ref= {a_ref} , dati[4]= {dati[4]}" );
WriteLine("Premi un tasto per continuare..." );
ReadLine();
}
}
}
Invocando il metodo GetArrayDiElementi2
usando la variabile local ref
i cambiamente fatti su a_ref
verrano mantenuti anche nell' array,
a_ref=90
comporta che anche dati[4]è == 90
.
Grazie all'uso dei riferimenti locali e dei valori restituiti per riferimento si può fare a meno di usare codice unsafe e la sintassi è più semplice.