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

Visual Sudio 2017 ottiene upgrade con una certa cadenza e Microsfot ha deciso di fare lo stesso con C# rilasciando a punti upgrade minori .x con x versione minor dell'aggiornamento aggiornamento. Comunque Visual Studio 2017 permette di decidere se passare alla versire minor oppure mantenere sempre come predefinia la versione major C# 7.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.