Home » Approfondimenti » Computer Science » C# fundamentals
  []

  1. C# main features (Tipi primitivi, Tipi non primitivi)
  2. .NET Preview (.NET Versions, CLI, DLL, IL Code, CLR, ILDasm, Variabili di tipo Var, Dynamic,, Reflexion)
  3. Advanced features (Yield, Generics, Espressions Lamba, Anonymous Type, Extension Methods, Covariance, Equals vs ==, Delegates, Events, Expression Tree)
  4. Regular Expressions
  5. Programmazione asincrona (TAP – Task-based asynchronous programming)
  6. Gestione dei nullable
  7. Custom snippet
  8. LINQ
  9. Caching
  10. Exception Handling
  11. Preproccessor
  12. Automapper
  13. Serialization in NET
  14. OData
  15. OWIN
  16. Finalize / Dispose
  17. Threading
  18. Collection (Array, ArrayList, Hashtable, Stack / Queue, Specialized collections, Generic collections)
  19. Novità C# (Records, Pattern matching enhancements, Global using directives, Null coalescing assignements (?? e ??=), Null propagator, Advanced Tuples, Deconstruct; Discard, Local functions, Expession-bodied members, Ref locals and Ref return)
  20. Benchmark.net
  21. Risorse

 
C# main features (Tipi primitivi, Tipi non primitivi)

  • Tutti i tipi in .NET ereditano dal tipo system.Object.
  • Convenzione per i nomi :
    • Non possono iniziare con numeri e contenere spazi.
    • Non si possono usare keyword come identifier (nel caso usare @int).
    • Camel case
      • variabili (int firstNumber).
    • Pascal case :
      • costanti (const int FirstNumber).
    • Hungarian notation :
      • non usato in c# (es: strFirstName).
  • Tipi Primitivi  =>

  C# type .Net type Bytes Range
Integer byte Byte 1 0..255
  short Int16 2 -(2^16)/2 to (2^16)/2-1
  int Int32 4 default for integer => -(2^32)/2 to (2^32)/2-1
  long Int64 8 -(2^64)/2 to (2^64)/2-1
Real

numbers

float Single 4 da assegnare esplicitamente (es: float number = 1.2f)

precision 7 decimali

  double Double 8 default for real numbers (es: double number = 1.2)

precision 16 decimali

  decimal Decimal 16 da assegnare esplicitamente (es: decimal number = 1.2m)

presione 29 decimali

* preferire questo per valori legati ai soldi *

decimal != float or double (non accadrà mai che 3 diventa 2.99999)

Characters char Char 2 Unicode characters
Boolean bool Boolean 1 true/false
    • Sono i tipi definiti nativamente dal linguaggio stesso.
    • Sono tutti degli struct  e quindi dei tipi valore .
    • Struct
      • Tipo valore compositi.
      • Differiscono dalle classi semanticamente.
      • Gli struct sono allocati nello stack.
      • L’allocazione della memoria è automatica.
      • La memoria è immediatamente rimossa quando lo struct è fuori dallo scope.
      • Utili per piccole strutture dati accedute tante volte.
      • Sono utili nella programmazione parallela (TPL): 
        • es: ValueTask, ValueTuple.
    • Unsigned vs Signed =>
      • I valori numerici posso essere definiti come Unsigned, significa cioé che non viene utilizzato spazio in memorica per ospitare il segno.
      • In una variabile numerica unsigned tutti i byte sono per ospitarne il valore (partendo da 0, non c’è bisogno di avere i valori negativi).
      • In una variabile numerica signed 1 bit contiente il segno. Perciò il range di possibili valori sarà inferiore che in un tipo unsigned  dovendo ospitare ancne tutti i valori negativi.
      • CTS (Common type System) :
          • .NET supporta molti linguaggi grazie al CTS
        • Durante la compilazione esiste una uniformazione dei tipi usati nei vari programmi verso un tipo comune (colonna .Net type)
      • CLS (Common language specification):
        • Insieme di regole che permetto l’uniformazione che il compilatore effettua quando converte il codice scritto in differenti linguaggi in IL (intermediate language)
        • //E' possibile verificare se ho seguito le regole specificate dal CLS
          [assembly: CLSCompliant(true)]
          namespace MyNamespace
          {
            ...
          }
    • Overflow :
      • Oltrepassare il limite del tipo di dato.
      • In C# non c’è il controllo di overflow di default
        • Usare checked per forzare il programma a lanciare un’eccezione in fase di coding.
        • checked
          {
            byte number = 255;
            number = number + 1;
          }
    • Casting (tipi compatibili) :
      • Casting implicito :
        • Il compilatore mi lascia passare da un tipo di dati più piccolo a uno più grande senza alcun problema.
        • byte b = 1;
          int i = b;
      • Casting esplicito :
        • Per passare invece da un dato più grande a uno più piccolo devo fare il casting explicito altrimenti il compiler s’incazza
        • int i = 1;
          byte b = (byte)i;
    • Conversion (tipi non compatibili) =>
      • - Convert.ToInt32
        - int.Parse
    • Type inference :
      • Il compilatore può dedurre il tipo di del lato sinistro di un’espressione da quello del lato destro
      • var a = b + 100; (a sarà allora di tipo Int);
    • Operatori =>
      • Aritmetici =>
        • somma (+)
        • differenza (-)
        • moltiplicazione (*)
        • divisione (/)
        • modulo (%)
        • incremento
          • prefix (es: ++a)
          • postfix (es: a++)
        • decremento
          • prefix (es: –a)
          • postfix (es; a–)
      • Confronto =>
        • ==, !=, >, <, >=, <=
      • Assegnamento =>
        • =
        • += (es a += 3 => a= a+3)
        • -=
        • *=
        • /=
      • Logici =>
        • && (and)
        • || (or)
        • ! (not)
      • Bitwise =>
        • & (and)
        • | (or)
  • Tipi non primitivi (Class, struct,  Dynamic, Record, Array, List, String Enum) =>
    • Sono tutti i tipi non definiti nativamente dal linguaggio.
    • Class :
      • Tipo riferimento
      • Quando istanzio una nuova classe con la parola chiave new, viene allocata della memoria heap per contenere la nuova istanza del mio oggetto.
        • Pulita poi dal Garbage Collector.
      • Definizione
        • Access modifier + class + identifier.
        • es: public class MyClass.
      • Field/attributi.
      • Metodi.
      • es: Task, Tuple.
    • Struct
      • Tipo valore compositi.
      • Gli struct sono allocati nello stack.
      • Differiscono dalle classi semanticamente.
      • L’allocazione della memoria è automatica.
      • La memoria è immediatamente rimossa quando lo struct è fuori dallo scope.
      • Utili per piccole strutture dati accedute tante volte.
      • Sono utili nella programmazione parallela (TPL) 
        • es: ValueTask, ValueTuple
      • No a ereditarietà e polimorfismo.
      • a implementazione interfacce.
    • Dynamic :
      • Una variabile di tipo dynamic prenderà il suo tipo solo a runtime.
      • Linguaggi tipati staticamente :
        • I tipi sono assegnati a compile time (C#, Java).
        • Vantaggi => feedback immediato.
      • Linguaggi tipati dinamicamente :
        • I tipi sono assegnati a runtime (Python, Javascript).
        • Vantaggi => codice più facile.
      • > .NET 4.0 :
        • E’ stata aggiunta la possibilità di utilizzare la tipizzazione dinamica per migliore l’interoperabilità (e evitare di essere forzati a usare reflexion).
          • con oggetti COM (esempio Office)
          • con linguaggi a tipizzazione dinamica (come Javascript)
            • Edge-js => libreria che permette di eseguire codice javascript in .NET e viceversa.
      • Non necessitano conversione esplicita:
        • int i=5
          dynamic d=i;
    • Record :

 

      • Sono by default tipi riferimento immutabili (soprattutto se si sfrutta la posizionalità per definire le variabili).
      • E’ usato per flussi unidirezionali (come gli oggetti DTO).
      • Equità per valore (come nelle variabili di tipo valore)
        • Due variabili di un tipo di record sono uguali se i tipi corrispondono e tutti i valori di proprietà e campo corrispondono.
        • A differenza invece degli altri tipi di riferimento dove uguaglianza significa identità. Cioè, due variabili di un tipo di riferimento sono uguali se fanno riferimento allo stesso oggetto.
      • E’ possibile definire le proprietà in funzione alla loro posizione (senza dovere usare il loro nome)
        • public record Person(string FirstName, string LastName);
          
          public static void Main()
          {
            Person person = new("Nancy", "Davolio");
            Console.WriteLine(person);
            // output: Person { FirstName = Nancy, LastName = Davolio }
          }
    • Class vs Struct vs Record :

    • Array :
      • Struttura dati per immagazzinare una collezione di variabili dello stesso tipo.
      • Dimensione fissa.
      • Tipo riferimento.
      • Metodi di un array =>
        • Length :
          • my_array.Length() //ritorna la dimensione dell'array
        • IndexOf :
          • Array.IndexOf(my_array, 9)
            //ricerca il valore 9 nell'array e torna in quale posizione si trova
            //se il valore 9 non c'è, ritorna -1
        • Clear :
          • Array.Clear(my_array, first_position_to_clear, lenght_to_clear) 
            //azzera <lenght_to_clear> valori dell'array a partire dalla <first_position_to_clear>
        • Copy :
          • Array.Copy(array_source, array_dest, num_position_to_copy)
            //copia <num_position_to_copy> dall'array source all'array dest
        • Sort / Reverse :
          • Array.Sort(my_array) //ordina piccolo->grande
            Array.Reverse(my_array) //ordina grande->piccolo
      •  Monodimensionale
        • int[] aInteri = new int[7] { 1, 2, 3, 4, 5, 6, 7 };
          (implicit) var numbers = new[] { 1, 2, 3, 4, 5, 6, 7 };
      • Multidimensionale
        • Rettangolare (matrice) :
          • //matrice con 3 righe e 5 colonne
            int[,] matrice3x5 = new int[3, 5] { 
               { 1, 2, 3, 4, 5 }, 
               { 6, 7, 8, 9, 10 },
               { 11, 12, 13, 14, 15 }
            };
        • Jagged (array di array) :
          • var jagged = new int[3][];
            jagged[0] = new int[4] { 0, 1, 2, 3 };
            jagged[1] = new int[5] { 4, 5, 6, 7, 8 };
            jagged[2] = new int[3] { 9, 10, 11 };
            
            //implicit declaration
            var jagged = new int[][]
            {
                new[] { 0, 1, 2, 3 },
                new[] { 4, 5, 6, 7, 8 }, 
                new[] { 9, 10, 11 }
            };
    • List :
      • Come gli array, le liste sono strutture dati per immagazzinare una collezione di variabili dello stesso tipo ma di dimensione dinamica.  
      • Dimensione dinamica
      • Tipo riferimento
      • Metodi di una list =>
        • Add :
          • Aggiunge l’elemento specificato in fondo alla lista.
        • AddRange :
          • Aggiunge una lista/array di elementi specificati in fondo alla lista.
        • Remove :
          • Rimuove dalla lista la prima occorrenza dell’elemento specificato.
          • Non usare foreach (ma for)
        • RemoveAt :
          • Rimuove dalla lista l’elemento nella posizione specificata.
        • IndexOf 
          • Ritorna la posizione della lista dove si trova l’elemento specificato (-1 se non trovato).
        • LastIndexOf
        • Contains :
          • Ritorna true o false se trova o meno nella lista l’oggetto specificato.
        • Count
          • Numero di oggetti nella lista.
        • Clear
          • Rimuove tutti gli elementi della lista.
        • Reverse
        • Sort 
    • String :
      • Tipo riferimento.
      • Sono immutable:
        • Una volta creato un oggetto di tipo string il suo valore non può essere modificato. 
        • Infatti se modifico una stringa, in realtà C# crea una nuova porzione di memoria, una nuova variabile, vera e propria.
      • Le stringhe in C# sono Unicode (supportano caratteri cinesi e emoticons).
      • Le stringhe in C# sono composta da caratteri di 2 byte.
      • Verbatim string :
        • Usare il carattere speciale ‘@’ per inizializzare una stringa di tipo verbatim (senza la necessità di fare l’escape dei caratteri special):
        • var mystring = @"c:\progetto\sottoprogetto\
                           questa è una nuova linea";
      • Escape character :
        • \n (new line).
        • \t (tab).
        • \\ (backslash).
        • \’ (single quotation mark).
        • \” (double quotation mark).
      • Formattare una stringa :
        • ToLower().
        • ToUpper().
        • Trim().
        • TrimEnd().
        • TrimStart().
      • Ricercare in una stringa :
        • IndexOf(<char_to_find>):
          • Ritorna la posizione della prima occorrenza.
        • LastIndexOf:
          • Ritorna la posizione dell’ultima occorrenza.
        • StartWith.
      • Substring:
        • var stringa = "Nicola";
          var primalettera = stringa[..1]; //N   (eq a substring(0,1))
          var senzaPrimaLettera = stringa[1..]; //icola  (eq a substring(1))
      • Replace :
        • Rimpiazza tutte le occorrenze del pattern ricercato.
      • string.IsNullOrEmpty e string.IsNullOrWhiteSpace
      • Split:
        • my_string.split(",")
      • TextBuilder :
        • Non ha metodi per cercare.
        • Append().
        • AppendLine()  (aggiunge <newline>).
        • Insert(<start_positon>, <valore_da_aggiungere>).
        • Remove(<start_position>, <lenght>).
        • Replace().
        • Clear().
    • Enum
      • Particolare tipo di dato costituito da una lista di nomi costanti che ne determina i possibili valori.
      • Inoltre ad ogni elemento della lista è associato un tipo di dato numerico (default è intero) che parte da 0 e si incrementa di 1 ad ogni voce della lista
      • Sarà sufficiente castare l’elemento della lista al tipo di dato associato per averne il valore numerico che gli corrisponde .
      • E’ consigliato definirlo a livello di namespace anche se è possibile definire un tipo enum anche all’interno di classi.
      • Agisce come un membro static (non deve essere instanziato) e sealed (non può essere derivato).
      • Un enum non può derivare che da uno dei tipi di dato numerico che può assumere (byte, sbyte, short, ushort, int, uint, long, ulong).
      • Deriva implicitamente da System.Enum.
      • Deriva dall’interfaccia IComparable, IFormattable, IConvertible.
      • Duplicità:
        • I nomi che costituiscono la lista dell’ enum non possono essere duplicati ma il valore assegnato sì.
      • Sovrascrivere valore di default:
        • Quando si definiscono i nomi della lista è possibile specificarne il valore (diverso da quello di default) che deve essere del tipo deciso per l’enum.
      • Favoriscono la leggibilità del codice.
      • Non ammettono la dipendenza circolare.
      • Agisce come una costante e i suoi valori non possono essere cambiati una volta inizializzati.
      • Esempio:
        • //Da int a enum
          int contactId = 1;
          ContactMethod contact = (ContactMethod)contactId;
          Console.WriteLine(contact.ToString()); //ByMail
          Console.WriteLine((int)contact); //1
          
          //Da string a enum
          string strShipping = "ByPhone";
          var shipType = (Shipping) Enum.Parse(typeof(Shipping), strShipping);
          Console.WriteLine((int)shipType);
          
          //Dichiaro enum
          public enum ContactMethod
          {
             ByMail =1,
             ByPhone =2
          }
  • Conditional statements :
    • if/else
    • switch/case
      • switch (number)
        {
          case 1: 
              ..
              break;
          case 2:
             ..
             break;
          default:
             ..
             break;
        }
    • a ? b : c
  • Iteration statement (In ogni tipo di loops è possibile usare break  e continue):
    • For.
    • Foreach.
    • While :
      • while(true)
        {
           Console.Write("Enter a name:");
           var name = Console.ReadLine();
           if(!string.IsNullOrWhiteSpace(name)){
              Console.Write("Benvenuto {0}: ", name);  
              continue;
           }
           break;
        }
    • Do-While :
      • E’ leggermente diverso da While perché viene eseguito almeno una volta essendo la condizione verificata alla fine.
  • DateTime :
    • Le date sono immutabili (una volta creato un oggetto di tipo DateTime il suo valore non può essere modificato):
      • my_date.AddHours(1)  //crea un nuovo oggetto con un un'ora in più
    • Esempio di formattazione di una data
      • my_date.ToString("yyyy-MM-dd HH:mm)
    • TimeSpan (serve per gestire la differenza tra due date). 
      • var timeSpan = DateTime.Now - this.BirthDate;
        var years = timeSpan.Days / 365;
  • Random :
    • Pseudo random => Usano un seed che è previsibile
      • public static string GetPseudoRandom()
        {
           //default seed
           var random = new Random();  //internalmente fa il seed prendendo come riferimento il time della CPU clock dell'host
           var valore1 = random.next();
           var valore2 = random.next();
           var valore3 = random.next();
          
           oppure
         
           //con un seed (es: 10, 20)
           var random = new Random(); 
           var valore1 = random.next(10);
           var valore2 = random.next(20);
        
           return valore1.ToString();
        }

    • Secure random => Usano un seed meno facile da prevedere
      • using System.Security.Cryptography;
        
        public static string GetSecureRandom()
        {
           //seed => OS entropy
           using (RNGCryptoServiceProvider provider = new RNGCryptoServiceProvider)
           {
              byte[] randomNumber = new byte[100000];
              provider.GetBytes(byteArray);
              //convert 4 bytes to an integer
              var value = BitConverter.ToUInt32(randomNumber, 0);
              return value.ToString();
           }
        }

    • Unique number =>
      • string str = Guid.NewGuid().ToString();

  • Boxing/Unboxing :
    • Boxing (Stack => Heap) :
      • Convertire un’istanza di tipo valore in un tipo referenza:
        • Il valore di tale istanza convertita in un tipo referenza non sarà più memorizzato nello stack ma nella memoria heap. 
        • int number = 10;
          Object obj = number;
    • Unboxing (Heap => Stack) :
      • Convertire un’istanza di tipo referenza in un tipo valore
        • Il valore di tale istanza convertita in un tipo referenza non sarà più memorizzato nello stack ma nella memoria heap
        • Object obj = 10;
          int number = (int)obj;
    • Attenzione : entrambe queste operazioni hanno un impatto sulle performance.
  • Generics :
    • E’ un modo per passare come parametro non solo dei valori ma dei tipi .
    • //definisco un delegate e un metodo che lo usa
      
      - CASO 1 => modo classico
      delegate int MathOp(int a, int b);
      
      void CalculateAndPrint(int a, int b, MathOp f)
      {
        var result = f(mermber1, member2);
        Console.WriteLine(result);
      }
      
      CalculateAndPrint(1, 2, (x,y) => x + y);  //result => 3
      
      
      - CASO 2 => uso generics
      delegate T MathOpGen<T><T a, T b);
      
      void CalculateAndPrint<T>(T a, T b, MathOpGen<T> f)
      {
        var result = f(mermber1, member2);
        Console.WriteLine(result);
      }
      
      CalculateAndPrint("A", "B", (x,y) => x + y); //result => "AB"
  • File (System.IO) =>
    • File  :
      • Usa metodi statici.
      • Adatto per trattare piccole quantità di file.
    • FileInfo  :
      • Usa metodi che vanno istanziati.
      • Adatta per operazioni più pesanti.
    • File, FileInfo => 
      • Create()
      • Copy(<source_file_path>, <dest_file_path>, <force_overwrite>)
      • Delete()
      • Exists()
      • GetAttributes()
      • Move()
      • ReadAllText()
      • ReadAllBytes()
      • WriteAllText()
      • WriteAllLines()
    • Directory :
      • Usa metodi statici.
    • DirectoryInfo :
      • Usa medoti da istanziare.
    • Directory, DirectoryInfo :
      • CreateDirectory()
      • Delete()
      • Exists()
      • GetCurrentDirectory()
      • GetFiles() => ritorna i file presenti nella cartella (e sottocartelle se desiderato)
      • GetDirectories() => ritorna le cartelle presenti nella cartella (e sottocartelle se desiderato)
      • Move()
      • GetLogicalDrives()
    • Path (permette di lavorare con una stringa che rappresenta un path):
      • GetDirectoryName()
      • GetFileName()
      • GetFileNameWhitoutExtension()
      • GetTempPath()
      • GetExtension()
        • var ext = Path.GetExtension(source);
          Console.WriteLine(ext);
      • Esempio:
        • using System.IO
          
          class Program(string[] args)
          {
             //Step 1
             Directory.CreateDirectory(@"c:\newfolder");
           
             //Step 2 File create and write
             File.WriteAllText(@"c:\newfolder\newfile.txt", "some context here");
          
             //Step 3 File create and write multiple lines
             string[] arr = { "line1", "line2", "lineN" };
             File.WriteAllLines(@"c:\newfolder\newfile.txt", arr);
          
             //Step 4 Visualizzo tutte le directory presenti in c:\
             string[] allfolders = Directory.GetDirectories("c:\")
             foreach(string folder in allfolders)
             {
                Console.WriteLine(folder);
                
                //Step 5 - visualizzo tutti i file nella cartella corrente
                string[] allFiles = Directory.GetFiles();
                foreach(string file in allFiles)
                {
                     Console.WriteLine(folder);
                }
             }
          
             //Step 6 - read file content
             string content = File.ReadAllText(@"c:\newfolder\newfile.txt");
             
             oppure
          
             IEnumerable<string> contentLines = File.ReadLines(@"c:\newfolder\newfile.txt");
             foreach(string line in contentLines)
             {
                Console.WriteLine(line);
             }
          
             //Step 7 - leggo le informazioni di una directory
             DirectoryInfo fi = new DirectoryInfo(@"c:\newfolder");
             Console.WriteLine(di.Exists);
             Console.WriteLine(di.LastAccessTime);
          
             //Step 8 - leggo le informazioni di un file
             FileInfo fi = new FileInfo(@"c:\newfolder\newfile.txt");
             Console.WriteLine(fi.Exists);
             Console.WriteLine(fi.Extension);   
          }

 

.NET Preview (.NET Versions, CLI, DLL, IL Code, CLR, ILDasm, Variabili di tipo Var, Dynamic, Reflexion)

  • .NET Infrastructure => base class library  (biblioteque logicielle) + common language machine (CLR)
    • .NET Installation =>
      • CLR runtime
      • SDK 
        • C:\Program Files\dotnet\sdk
    • .NET Versions =>
      • Microsoft sviluppa e mantiene 4 implementazioni di .NET  (piattaforme per eseguire programmi .NET)
      • .NET Framework 
        • Nel 2001 Microsoft voleva fronteggiare l’ascesa di JAVA e superare la situazione in cui era con VB e C++ allora ha creato la piattaforma .NET e la sua implementazione per il sistema operativo windows .NET Framework.
        • E’ una piattaforma per lo sviuluppo di applicazioni
          • WEB
          • Windows
          • Windows Server
          • Azure App
        • Supporta molti linguaggi
          • C#
          • F#
          • VB.NET
          • Linguaggi di terze parti che hanno bisogno del loro compilatore
        • Offre una serie di .NET Services
          • Memory management
          • Type and memory safe
          • Security
          • Networking
          • Application deployment
        • TPL (Task Parallel Library) (2010)
        • TAP (Task-base Asyncronous programming) (2012)

 

      • .NET Core (>2016)
        • Cambio strategia di business =>
          • Nel 2016 Microsoft ha cambiato la sua strategia di business passando dalle vendite di licenze windows alle licenze cloud non vedendo un futuro commerciale se fosse rimasta immobile.
          • Per questo essendo Linux il SO per il cloud ha deciso che per la sopravvivenza di .NET bisognasse andare verso il cross-platform e l’open source
          • NET Core è quindi l’Implementazione di .NET cross-platform e open-source diretta di Microsoft.
          • .NET Foundation => Sviluppa l’eco sistema .NET.  E’ un’entità collegata a Microsoft ma esterna. Si sono riavvicinati alla MONO community.
        • Nuova Versione più light =>
          • Ridisegnando il compiler, La CLR e le librerie di base, Microsoft ha approfittato per mantenere il cuore e abbandonare alcune implementazioni sbagliate o non fondamentali, riducendo l’implementazione complessiva.
          • CoreCLR (Core common language Machine) + CoreFX (Core base class library)
          • .NET Core SDK.
        • Visual Studio Code =>
          • Microsoft ha sviluppato un nuovo e completo IDE open-source e free
          • //nella shell mi posizione nella cartella del progetto che voglio aprire
            c:\myproject>code .   //apro il mio progetto in Visual Studio Code
          • //Inizializzare "VISUAL STUDIO CODE" per creare una ASP.NET Core Web Api
            - installare l'estensione 'C#'
            - installare l'estensione 'Nuget Package Manager'
            - eseguire i comandi CLI: 
               - dotnet new <tipo-progetto> //creare un nuovo progetto .NET tramite comando CLI 
               - dotnet tool install --global dotnet-ef  //installo il Command Shell Tool per EF
               - dotnet add package Microsoft.EntityFramework.Design //installo package per EF
               - dotnet add package Microsoft.EntityFramework.SqlServer //installo package per SqlServer
               - dotnet ef //verifico che l'installazione sia andata a buon termine
               - dotnet restore //reinizializzo tutte le "dipendenze" del progetto
            - database-first (scaffold database esistente)
               - dotnet ef dbcontext scaffold "Data Source=.;Initial Catalog=MangoOrderAPI;
                    Persist Security Info=true;User ID=sa;Password=Halifax1_;Trust Server Certificate=true" 
                    Microsoft.EntityFramework.SqlServer --output-dir Models
            
            
            //<my-project>.csproj
            <Project>
              <ItemGroup>
                 <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.5">
                    <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
                    <PrivateAssets>all</PrivateAssets>
                 </PackageReference>
                 <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.5" />
              </ItemGroup>
            </Project>
        • Linux Kernel =>
          • Nelle ultime versioni di Windows esiste un kernel Linux per eseguire applicazioni Linux/Docker deamons/Debian/Ubuntu nativamente senza la necessità di utilizzare VM.
        • .NET Source Browser =>
          • Un sito per visualizzare il codice sorgente del codice .NET.
      • Mono 
        • Essendo il CRL open-source e visto che .NET Framework è un’implementazione della CLR solo per Windows, la open-source community ha creato un’implementazione eseguibile in ambiente Linux.
        • Per applicazioni Xamarin che girano su altri SO
          • Xamarin.iOS
          • Xamarin.Mac
          • Xamarin.Android
      • Universal Windows Platform (UWP, per lo sviluppo di applicazioni per il  SO Windows)
      • Unity
        • Piattaforma molto usata per scrivere programmi in 2D o 3D come giochi o programmi si simulazione.
      • .NET 5 o 6 (rappresentano il presente e futuro della piattaforma .NET)
        • Raggruppa .NET core + .NET standard
        • Un programma scritto in .NET 5 funziona su molti tipi di device e ambienti differenti che lo supportino 
          • Mac
          • Android
          • Browser
          • TV
          • Ios devices
          • Unity
        • 2 runtime machines
          • Mono
          • CoreCLR
        • .NET Standard =>
          • E’ possibile scrivere delle class library per .NET standard (creando cioè un set standardizzato di APIs) che possano essere utilizzate/compatibili su tutte le 4 implementazioni di .NET
          • .NET implementation MATRIX
          • In base a quale versione di .NET standard si fa riferimento, la nostra class library sarà compatibile con le vecchie piattaforme .NET e le loro differenti versioni.
          • Ad esempio potrei voler scrivere una classe che interpreti delle immagini riconoscendo i cani dai gatti e renderla disponibile per Unity, per iOS, per un’applicazione Windows o un’applicazione web, ecc..
          • >= .NET Standard 2.1: persa definitivamente la compatibilità con .NET Framework.
    • Politiche di support 
      • LTS (Long Term Support)
      • Ci dice quando finirà il supporto di una data versione di .NET, utile per realizzare applicazioni che funzioneranno nel tempo.
    • Quale .NET usare ?
      • Nuove applicazioni e librerie =>.NET 6 (ultima versione disponibile)
      • Compatibilità con .NET Core => .NET standard 2.1
      • Compatibilità con .NET Framework =>  .NET standard 2.0
      • Target Framework monikers
      • Target framework Latest
        stable version
        Target framework moniker (TFM) Implemented
        .NET Standard version
        .NET 6 6 net6.0 2.1
        .NET 5 5 net5.0 2.1
        .NET Standard 2.1 netstandard2.1 N/A
        .NET Core 3.1 netcoreapp3.1 2.1
        .NET Framework 4.8 net48 2.0
  • CLI (Common Language Infracstuture) => CIL (Common Intermediate Language) + CLR (Common Language Runtime)  
    • Compilazione in 2 step (secondo un concetto già sfruttato da Java, un programma .NET non è compilato direttamente in codice nativo ma in un codice intermedio che è indipendente dal sistema operativo. Sarà questo codice intermedio a essere eseguito dalla CLR sul sistema operativo target, creando l’eseguibile corrispondente) =>
      1. Compilazione:  Codice di alto livello (es: C#) => IL code 
      2. Creazione eseguibile tramite CLR sul S.O. target:  IL code => exe file 
  • DLL (Dynamic-link Library) =>
    • E’ l’implementazione in ambiente Microsoft delle Shared Library di Java (librerie condivise).
    • Indica una libreria software che viene caricata dinamicamente in fase di esecuzione invece di essere collegata staticamente a un eseguibile in fase di compilazione.
    • Possono contenere codice (compilato), dati e file
    • Normalmente hanno l’estensioni:
      • .exe (viene eseguito nel proprio spazio di memoria)
      • .dll (deve essere eseguito nello spazio di memoria di un eseguibile)
      • .ocx
      • .drv 
    • Assembly => un pezzo di codice .net compilato. 
    • Non è possibile eseguire direttamente una dll ma è possibile farlo tramite utility come rundll.exe o rundll32.rxr che forniscono il punto di accesso per poterle eseguire
    • Vantaggi :
      • Ottimizzazione :
        • Permettono di suddividere il codice eseguibile in parti concettualmente separate, che verranno caricate solo se effettivamente necessarie.
      • Riusabilità :
        • Una singola dll caricata in memoria può essere utilizzata da più programmi senza la necessità di essere nuovamente caricata, il che permette di risparmiare le risorse del sistema.
      • Loading on demand :
        • Consente installazioni parziali di un sistema software, in cui sono effettivamente presenti sulla memoria di massa solo le librerie associate alle funzioni che l’utente desidera utilizzare, come selezionate in fase di installazione.
      • E’ possibile sostituire una dll utilizzata da un programma senza doverlo ricompilare.
    • Svantaggi :
      • DLL hell:
        • Quando il sistema operativo carica (perché ha disponibile) una versione della dll differente da quella attesa dal programma che si sta eseguendo. 
      • Breaking changes:
        • Installazioni di versioni successive della stessa dll potrebbero causare malfunzionamenti nei programmi che le usano.
  • Compilazione  (bin vs obj) :
    • Quando si compila un progetto .NET si creano 2 cartelle:
      • bin 
        • Debug
        • Release
      • obj 
        • Debug
        • Release
    • In realtà la compilazione in c# è un processo incrementale :
      • Fase di compilazione :
        • Nella fase di compilazione vengono compilati individualmente i files e messi nella cartella obj.
      • Fase di linking
        • Nella fase di linking i file individuali compilati sono collegati in un’unità singola DLL o EXE e tale file è messo nella cartella bin

    • Conditional compiling :
      • Il fatto di avere diviso la compilazione in 2 passaggi e memorizzare i risultati i 2 cartelle distinte permette la compilazione condizionale.
      • Semplicemente se modifico solo 1 file, allora il compilatore sostituirà solo tale file in obj e aggiornerà il file dll presenti in bin

  • IL Code (Common Intermediate Language) =>
    • E’ il linguaggio di programmazione più basso della piattaforma .NET.
    • Deriva dalla compilazione del codice di alto livello scritto in uno dei linguaggi disponibili (C#, F#, Vb.NET, Managed C++).
    • E’ l’equivale del bytecode in Java. 
  • CLR (Common Language Runtime) =>
    • E’ la macchina virtuale del .NET Framework che gestisce l’esecuzione dei programmi .NET.
    • Traduce i file compilati nel linguaggio intermedio (IL) nel linguaggio macchina del sistema operativo nativo desiderato.
    • Questo processo di traduzione è detto compilazione Just-In-Time (JIT) :
      • Permette un tipo di compilazione, conosciuta anche come traduzione dinamica effettuata durante l’esecuzione del programma piuttosto che precedentemente.
      • L’obiettivo finale dei sistemi JIT è di combinare i vantaggi della compilazione del bytecode a quelli della compilazione nativa, aumentando le prestazioni quasi al pari di una compilazione direttamente in linguaggio macchina.
      • JIT :
        • La compilazione inizia allo start dell’applicazione e prosegue on-demand.
        • Il codice  è compilato + cache (GAC  c:\windows\assembly).
      • Pre-JIT (Ngen.exe) 
        • L’applicazione è precompilata interamente prima di essere eseguita.
        • //cmd prompt di visual studio
          > ngen install <path-my-app>
      • Eco-JIT 
        • Compilazione senza cache.
      • Visualizzare le performance :
        • Eseguire perfmon.exe e selezionare il tipo di performance da monitorare e quale applicazione monitorare:
        • //cmd promot di windows
          > perfmon
    • La CLR fornisce inoltre servizi addizionali come:
      • Memory management
      • Type safety
        • CTS (Common type System)
        • CAS (Code access security)
      • Exception handling
      • Security
      • Thread management
      • Garbage collector:   
        • E’ un sottosistema di gestione automatica della memoria implementato da un processo eseguito in background che viene eseguito in modo indeterministico responsabile del riciclo della memoria precedentemente allocato e poi inutilizzato.
        • Si attiva aleatoriamente quando vede che ce n’è bisogno.
        • Allocazione di memoria:
          • Quando si inizializza un nuovo processo il CLR (Common Language Runtime) riserva una regione di memoria contigua detta managed heap (mucchio gestito).
          • Il managed heap mantiene un puntatore all’indirizzo dove il nuovo oggetto nella memoria gestita verrà allocato e memorizzato.
          • Così facendo i processi di scrittura sono velocizzati rispetto a una gestione della memoria non gestita.
        • Rilascio di memoria:
          • Il CLR esamina l’ application root (l’elenco dei punti del memory heap che contengono un oggetto).
          • Rilascia la memoria di tutti gli oggetti che non ne fanno più parte che cioè non sono più usati dall’applicazione.
          • Se la memoria rilasciata è significativa allora rialloca gli oggetti ancora in uso e ridetermina managed heap pointer per il prossimo oggetto. 
        • Gen 0 , Gen 1, Gen 2 :
          • Gli oggetti in memoria sono divisi logicamente in base a quanto durano in memoria.
          • E’ un meccanismo per cui gli oggetti appena creati sono messi in un contenitore detto gen0.
          • Gli oggetti non più usati vengono rilasciati.
          • Gli oggetti ancora usati vengono spostati in un secondo contenitore logico gen1.
          • A questo punto il processo si ripete e nel caso tali oggetti siano ancora in uso sono messi in un terzo contenitore detto gen2.
            • Un esempio di variabili che sono gestire dal GC nel contenitore gen2 sono le variabili globali che vivono per tutta la vita dell’applicazione.
        • Monitorare l’utilizzo della memoria:
          • Scaricare e eseguire il profile ( CLRProfiler.exe) per monitorare l’utilizzo da parte del GC di gen0, gen1, gen2
          • // 
            // durante l'eseguizione di un'applicazione .NET
            CLRProfiler.exe
        • Metodo Dispose (IDisposable):
          • Il metodo disposo va esplicitamente richiamato affiché sia eseguito.
          • Permette di ottimizzare le risorse aumentando le performance del GC, facendolo lavorare meno.
          • Infatti se una classe utilizza (referenzia) delle risorse unmanaged e non si vuole attendere il GC è necessario implementare l’interfaccia IDisposable (e conseguentemente definire il metodo Dispose()).
          • Così facendo è possibile rilasciare risorse unmanaged in modo deterministico.
          • class Program
            {
               static void Main(string[] args)
               {
                  for(i=0, i< 100000, i++)
                  {
                     //uso l'oggetto
                     var cl1 = new MyClass();
            
                     //lancio il metodo dispose per liberare le risorse
                     cl1.Dispose();
                  }
               }
            
               public class MyClass:IDisposable
               {
                  public void Dispose()
                  {
                    GC.SuppressFinalize(true);
                  }
               }
            }
        • Metodo Finalize (class object)
          • Si scrive un finalizer per una classe se questa ha delle referenze a risorse unmanaged e si vuole essere sicuri che il GC rilasci queste risorse quando rilascerà l’instanza delle classe.
          • Non è possibile chiamare il Finalizer di un oggetto esplicitamente ma lo farà automaticamente il GC.  Senza nessun intervento da parte dello sviluppatore.
          • class ClassWithDisposeAndFinalize : IDisposable 
            { 
               // Used to determine if Dispose() has already been called, so that the finalizer 
               // knows if it needs to clean up unmanaged resources. 
               private bool disposed = false; 
            
               public void Dispose() 
               { 
                  // Call our shared helper method. 
                  // Specifying "true" signifies that the object user triggered the cleanup. 
                  CleanUp(true); 
                  
                  // Now suppress finalization to make sure that the Finalize method 
                  // doesn't attempt to clean up unmanaged resources. 
                  GC.SuppressFinalize(this); 
               } 
              
               private void CleanUp(bool disposing) { 
                  // Be sure we have not already been disposed! 
                  if (!this.disposed) 
                  { 
                     // If disposing equals true i.e. if disposed explicitly, dispose all 
                     // managed resources. 
                     if (disposing) 
                     { 
                        // Dispose managed resources. 
                     } 
                     // Clean up unmanaged resources here. 
                  } 
                  disposed = true; 
               } 
               
               // the below is called the destructor or Finalizer 
               ~ClassWithDisposeAndFinalize() 
               { 
                  // Call our shared helper method. 
                  // Specifying "false" signifies that the GC triggered the cleanup. 
                  CleanUp(false); 
               }
            }
  • ILDasm  / ILSpy (dll => IL code)
    • E’ un common intermediate language (CIL) disassembler fornito in C# che permette di visualizzare il codice IL a partire da un exe portatile o una dll.
    • VS -> Tools -> External tools section -> open IL Disassembler -> open file .dll 
      • Install-Package Microsoft.NETCore.ILDAsm -Version 6.0.0
        
        //Path nel computer locale
        C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\ildasm.exe
  • dnSpy (dll => codice) 
    • E’ un disassembler per passare da una dll compilata in debug mode o release mode al codice sorgente (in c# o vb.net) o il codice intermedio IL.
  • Application Domain :
    • E’ un meccanismo usato dal CLR per isolare l’esecuzione del codice all’interno di un confine sicurizzato.
    • Si crea cosi’ un container logicamente isolato nel quale il codice .NET viene eseguito indipendentemente.
    • Ogni dominio di applicazione risulta cosi avere ha una propria area di memoria virtuale.
    • Risulta essere quindi un meccanismo simile ai processi nel sistema operativo, usato dalla CLR per isolare l’esecuzione di ogni applicazione dalle altre così che non si disturbino.
  • Managed code :
    • Nella terminologia di Microsoft è un codice sorgente che ha bisogno della CLR per essere eseguito.
    • E’ un’applicazione che per funzionare ha bisogno del completo controllo da parte della CLR .
  • Unmanaged code
    • E’ del codice che é eseguito direttamente dal processore (fuori dalla responsabilità della CLR).
  • Unsafe code :
    • E’ a metà strada tra codice managed e unmanaged.
    • Infatti viene eseguito dalla CLR ma l’accesso delle memoria é gestito dallo sviluppatore tramite i puntatori.
    • E’ possibile scrivere del codice non gestito (using safe { … } ).
    • Se uso una risorsa non gestita (visto che non interviene il GC) è necessario esplicitare la liberazione della memoria:
      • using (FileStream stream = GetFileStream(context)) { // Operations on the stream } or stream.dispose();
  • .NET CLI (Command line interface per .NET) =>
    • E’ parte del .NET SDK 
    • Permette di eseguire tutta una serie di operazioni direttamente dalla SHELL senza la necessità di installare Visual Studio
    • dotnet --version //get the current installed .NET version
      dotnet new //questo comando visualizza la lista di possibili template 
      dotnet new console | mvc  -o <my-project-name>   //scaffolding per diversi tipi di applicazione
      dotnet restore //aggiorno il progetto con le nuove referenze 
      dotnet build
      dotnet run
      dotnet publish  //permette di pubblicare l'applicazione
      dotnet test //permette di eseguire i test automatici
      
      //creo 2 progetti e una solution con entrambi
      dotnet new webapi -n WebApplication
      dotnet new worker -n WorkerService
      dotnet new sln
      dotnet sln add WebApplication WorkerService
      
  • Packages :
    • Metapackage => Insieme di package
    • Package => Insieme di assembly (dll)

 

    • Nuget
      • E’ il gestore di pacchetti officiale per .NET.
      • E’ l’equivalente Microsoft di:
        • npm per Node.
        • maven per Java.
        • pip per Python. 
  • C# / VB.NET
    • Friend -> internal
      Me -> this
      MustInherit -> abstract
      MustOverride -> abstract
      MyBase -> base
      NotInheritable -> sealed
      NotOverridable -> sealed
      Overrdable -> virtual
      Share -> static
      Override -> Override
  • Variabili di tipo Var :
    • Danno la possibilità di definire il tipo di una variabile/oggetto in un modo indiretto tramite la type inference :
      • Il tipo è definito dal compiler in modo implicito dal contenuto della variabile stessa.
      • Il tipo è stabilito staticamente (a compile time)  e non dinamicamente (a runtime).
      • var conta = 0;
    • Vantaggi :
      • E’ utile quando per rimpiazzare nomi di classi troppo lunghi.
      • LINQ con Anonymous types.
      • Permette l’early-binding.
  • Variabili di tipo Dynamic  :
    • C# è un linguaggio fortemente tipato => il tipo degli oggetti e delle variabili sono stabiliti a compile time.
    • Javascript è un esempio di linguaggio dinamicamente tipato :
      • Il tipo delle variabili è stabilito a runtime.
      • Eventuali metodi sono invocati tramite reflexion
      • //a runtime viene usata la reflexion e viene invocato il metodo Lenght della variabile myString
        //una variabile dynamic al suo interno usata la reflexion per invocare i metodi
        dynamic myString= "string1";
        int length = myString.Lenght (runtime invoked)
    • Vantaggi =>
      • Invocare metodi pubblici dinamicamente.
      • Sfrutta la cache.
      • Usare quando si interagisce con componenti di Microsoft Office o di framework Javascript.
      • Permette il late-binding.
  • Reflexion :
    • Permette di esaminare a runtime il contenuto di un assembly
      • Metadata :
        • tramite il namespace System.Reflection il .NET Framework fornisce una serie di API per analizzare a runtime gli assembly e ottenere le informazioni relative ai tipi ivi contenuti 
        • 0 - importare l'opportuno namespace 
          using System.Reflection; 
          
          1- Caricare un assembly 
          Assembly myAssembly = Assembly.LoadFrom("myAssembly.dll"); 
          
          2- Recuperare la lista dei tipi o un tipo in particolare 
          var myTypesArray[] = myAssembly.GetTypes(); 
          oppure 
          var myType = asm.GetType("myAssembly.MyClass"); 
          
          3- Dal tipo posso istanziare l'oggetto corrispondente 
          dynamic myClassObj = Activator.CreateIstance(myType); 
          Type parameterType = myClassObj.GetType(); 
          
          4- Visualizzare i metadata 
          parameterType.GetMembers() : ritorna una array di oggetti System.Reflection.MemberInfo; 
          parameterType.GetFields() : ritorna una array di oggetti System.Reflection.FieldInfo; 
          parameterType.GetProperties() : ritorna una array di oggetti System.Reflection.PropertyInfo; 
          parameterType.GetMethods() : ritorna un array di oggetti System.Reflection.MethodInfo.
      • InvokeMember :  
        • consente eventualmente di invocare a runtime direttamente i metodi di una classe, o di accedere alle sue proprietà.
        • consente di modificarne le strutture di alto livello (intercessione)
        • 5 - è possibile invocare il 'Method1' presente nell'oggetto 'myClassObj'
          parameterType.InvokeMember("Method1",
                                     BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod | BindingFlags.Instance,
                                     null, myClassObj, null);
    • Vantaggi :
      • Permette l’intellisense in IDE come Visual Studio.
      • Permette di esaminare metodi privati durante lo svolgimento degli unit test.
      • Permette di conoscere i contenuti di un assembly per poter mettere in un file o visualizzarli a monitor.
    • Reflexion vs Dynamic :

  • Dipendenza circolare :
    • In un progetto a 3 layer se il Middlelayer usa dei componenti definiti nel DAL e viceversa, allora si avrà un caso di dipendenza circolare e Visual Studio ci impedisce di aggiungere la seconda referenza che chiuderebbe il cerchio
    • Soluzione tramite interfaccia 
      • La solazione è passare tramite un terzo componente che faccia da interfaccia. 
      • Gli oggetti presenti in Middleware e DAL che si volevano condividere dovranno implementare la nuova interfaccia.
      • Il Middleware e il DAL possono tranquillamente referenziare tale nuovo componente e comunicare tramite quello.
    • Soluzione tramite event
  • Build vs Rebuild vs Clean
    • Build => Compila solo le modifiche apportate al progetto corrente.
    • Rebuild => Clean + ricompila tutta la soluzione.
    • Clean => Elimina i file compilati precedentemente.

  • GetType != typeof =>
    • //GetType => runtime
      Customer customer = new Customer();
      Supplier supplier = new Supplier();
      
      //la somiglianza dei 2 tipi è valutata a runtime
      if(customer.GetType() == supplier.GetType())
      {
         ...
      }else{
         ...
      }
      
      //typeof => 
      Customer customer = new Customer();
      if(typeof(Supplier) == customer)
      {
         ...
      } else {
         ...
      }

  • Symbol file (PDB – Program Database)
    • Quando si compila un progetto .NET vengono creati dei file (PDB) detti “symbol” che permettono al debugger di leggere il codice compilato IL e di passare alla relativa istruzione di codice all’interno di Visual Studio.
    • Sono file che contengono informazioni utilizzate del debugger di Visual Studio per debuggare.
  • .NET Architecture
    • Applicazione => è un insieme di classi che collaborano tra loro per fornire le funzioni per cui sono scritte.
    • Classe => blocco che costituiscono un’applicazione. E’ formata da:
      • Metodi (funzioni) => implementano comportamenti / fanno qualcosa per noi.
      • Data (attributi) => rappresentano lo stato dell’applicazione.
    • Namespace => contenitori per classi correlate tra loro.
    • Assembly (exe o dll) => un file che contiene namespaces correlati tra loro e che fa parte del deploy di un’applicazione .NET;

 

Advanced features (Yield, Genercics, Espressions Lamba, Anonymous Type, Extension Methods, Covariance, Equals vs ==, Delegates, Events, Expression Tree)

  • Yield =>
    • Custom iteration con una lista temp in uscita :
      • Il comando yield è generalmente usato in una routine chiamata da clclo esterno (es: foreach) 
      • La parola chiave ‘yield’ può essere usata in ogni funzione che ritorna un IEnumerable.
      • Il ciclo esterno chiama ad ogni iterazione la routine dove è presente il comando yield:
        • Se la logica lo sottointende la routine chiamata restituisce un primo valore al ciclo esterno.
        • Il ciclo esterno continua così la sua esecuzione.
        • Alla chiamata successiva la routine comincia a valutare la collezione dal punto in cui si era interrotta alla chiamata precedente .
        • Il risultato finale sarà che la routine esterna avrà la somma dei valori restituiti dalla routine interna per ogni chiamata effettuata (come se ci fosse una variabile temp).
      • //Uso yield ad esempio per filtrare una collezione senza dover creare una collezione temp di appoggio
        class Program
        (
           public List<int> MyList = new List<int> { 1,2,3,4,5 };
           public void Main(strings[] args)
           {
               foreach(extItem int in Filter())
               {
                   Console.WriteLine(extItem);
               }
           }
        
           //senza yield
           //return 4,5
           static IEnumerable<int> Filter()
           {
              List<int> temp = new List<int>();
              foreach(int item in MyList)
              {
                 if(item>3)
                   temp.Add(item);
              } 
              return temp;
           }
        
           oppure
        
           //con yield
           //return 4 alla prima chiamata del ciclo esterno 
           //return 5 alla seconda chiamata del ciclo esterno
           static IEnumerable<int> Filter()
           {
              foreach(int item in MyList)
              {
                 if(item>3)
                   yield return item;
              } 
           }
        }

    • Stateful iteraton :
      • Se iterando una collezione ho bisogno di ricordare un valore (ad es: il prezzo totale degli articoli della collezione) posso:
        • Creare una routine che scorre la mia collezione e usare il comando yield per restituire il valore da ricordare
        • Iterare su questa routine per avere tutti i valore parziali da ricordare
      • //Uso yield per scorre una collezione e visualizzare la somma di un certo valore man mano che la collezione è scorsa
        class Program
        {
           public List<int> MyList = new List<int> { 1,2,3,4,5 }; 
           public void Main(strings[] args) 
           { 
               //il valore della variabile runningTotal presente nella routine RunningTotal()
               //è mantenuto ad ogni iterazione del cliclo esterno che la chiama
               foreach(currentTotal int in RunningTotal()) 
               { 
                    Console.WriteLine(currentTotal); 
               } 
           }
           
           static IEnumerable<int> RunningTotal()
           {
              int runningTotal = 0;
              foreach(int i in MyList)
              {
                 runninTotal += i;
                 yield return (runningTotal); 
              }
        
           }
        }
  • Genercics =>
    • public class Utilities<T> where T: IComparable, new()
      {
         public T Max(T a, T b)
         {
            return a.CompareTo(b) > 0 ? a : b;
         }
         public DoSomething(T value)
         {
            var obj = new T();
         }
      }
    • Constraints  (posso usare molti tipi di constraints per limitare i possibili input che posso assegnare alla variabile generica T):
      • where T : MyInterface (any interfacce)
      • where T:  Class (any reference type)
      • where T:  Struct (any value type)
      • where T: new() (la variabile può avere un costruttore)
    • Default => Posso usare la parola chiave per avere il valore di default del tipo desiderato
      • default(Int32) 
  • Espressioni Lamba =>
    • E’ un anonymous method con:
      • Nessun access modifier.
      • Nessun nome.
      • Nessuna parola chiave return .
      • Recupera il tipo dei parametri esplicitamente (a partire dal codice).
      • All’interno del metodo anonimo possiamo usare qualsiasi variabile definita nello scope (senza passarla esplicitamente al nostro metodo).
      • Diviso in 2 parti :
        • La parte a sx di ‘=>’
          • i parametri di input. 
        • La parte a dx di ‘=>’
          • il codice c# che implementa la logica desiderata.
    • Esempio sintassi : 
      • //caso generico: elenco argomenti => implementazione del metodo 
        //caso 0 parametro: () => ..    
        //caso 1 parametro: param => ..
        //caso molti parametri: (param1, param2) => ..
    • Esempio scrittura di un metodo :
      • // public delegate T Func();
        
        Func<int> theDelegate; // Declare a delegate that has no parameters and returns an integer value.
        
        theDelegate = NamedMethod; // Assign to a named method.
        theDelegate = delegate() { return 0; }; // Assign to anonymous method through delegate syntax.
        theDelegate = delegate { return 0; }; // It has no parameters, so round braces can be omitted.
        theDelegate = () => { return 0; }; // The same anonymous method through lambda expression syntax.
        theDelegate = () => 0; // The syntax of single return statement can be even more simpler.
        
        int result = theDelegate(); // Call the delegate and get the result.
        
        int NamedMethod() 
        { 
           return numero * numero; 
        }
      • int quadrato = Square(5); //25
        
        public static int Square(int numero)
        {
          return numero * numero; 
        }
        
        -- anonymous method
        var Square = (int numero) => { return numero * numero; }; 
        int quadrato = Square(4); //16
        
        --lamda expression
        var Square = (int numero) => (numero * numero); 
        int quadrato = Square(3); //9
    • Vantaggi :
      • Permetto di costruire Expression tree.
      • Semplificano le chiamate ai delegate.
  • Anonymous Type => 
    • E’ possibile creare delle istanze di oggetti anonimi, senza cioé aver definito a priori la classe di tali oggetti
    • Permetto di superare l’utilizzo delle Tuple
    • static object ParseDate(string strData)
      {
         string [] ArrayData = new string[3];
         ArrayData = strData.split(',');
         //anonymous type
         return new {
            FirstName = ArrayData[0],
            MiddleName = ArrayData[1], 
            LastName = ArrayData[2] 
         };
      }
  • Extension methods =>
    • Da usare per estendere le funzionalità di una classe che :
      • non può essere derivata (es: classe string)
      • non possiamo modificare (es: non ne abbiamo il codice sorgente)
      • non abusarne, perché Microsoft potrebbe aggiungere un metodo con la stessa signature del metodo extension implementato da noi
    • Standard da rispettare per creare un extension method :
      • Nuova classe
        • Nome classe
          • Creare una nuova classe chiamata <classe_da_estendere>Exentions .
          • E’ possibile chiamarla con lo stesso nome della classe originale.
        • Namespace
          • Tale classe deve essere inclusa nel namespace della classe da estendere solo nel caso si sia utilizzato il nome della classe originale
        • Tale classe deve essere dichiarats static.
        • Tale classe accoglierà tutti gli extension methods da implementare per estendere la classe base.
      • Nuovo extension method
        • Il nuovo  metodo d’estensione deve essere dichiarato static.
        • Il primo parametro deve avere parola chiave this ed essere del tipo <classe_da_estendere>.
        • All’interno del nuovo metodo é possibile richiamare i mebri public della <classe_da_estendere>. 
    • Library
      • Il metodo aggiunto alla <classe_da_estendere> viene aggiunto logicamente alla sua library anche se fisicamente presente in un file diverso.
    • Esempio di estensione della classe <String>  con il nuovo metodo StringExtensions.Shorten =>
      • using System.Text;
        
        string Post = "This is supposed to be a very long long sentence";
        var ShortPost = StringExtensions.Shorten(Post, 5);
        
        public static class StringExtensions
        {
          public static string Shorten(this String str, int NumberOfWords)
          {
            ...
            return res; 
          }
        }
  • Covariance / Contravariance (.NET 4.0) =>
    • Permettono di essere flessibili quando si tratta con gerarchia di classi.
    • Può essere applicata a:
      • Delegate
      • Genercs
      • Array
      • Interfacce.
    • Covariance
      • Passo un padre quando mi aspetto un figlio come parametro di output
      • Consente di utilizzare una classe derivata in cui è prevista una classe base durante il polimorfismo dinamico.
      • Implementata tramite parametro out.
    • Contravariance
      • Passo un figlio quando mi aspetto un padre come parametro di input
      • Implementata tramite parametro in.
    • //esempio di covariance e contravariance nell'implementazione di un delegate
      delegate Small covarDel(Big mc);
      
      class Program
      {
         //in questo esempio è usata sia 
         //la covariance (il metodo si aspetta come parametro di ritorno un figlo ma uso un padre)
         //che la contravariance (il metodo si aspetta come paramero di ingresso un padre ma passo un figlio)
         static Big Method4(Small sml) 
         {
             Console.WriteLine("Method3");
             return new Big();
         }
      
         static void Main(string[] args)
         {
             covarDel del = Method4;
             Small sm = del(new Big());
         }
      }
  • Equals vs == =>
    • Tipi primitivi :
      • Confronto matematico => Fanno un confronto a livello matematico
      • int i = 0;
        int j = 0;
        
        Console.Write((i==j));  //true      //confronto matematico
        Console.Write(i.Equals(j)) //true   //confronto matematico
    • Classi :
      • C#  può confrontare 2 oggetti solo confrontando l’indirizzo memorizzato nelle loro 2 variabili
      • E’ possibile customizzare la logica del metodo Equals() così creare un confronto da una prospettiva che rispecchi il mondo reale e non solo da una prospettiva tecnica
      • Confronto semantico => Customizzando la logica del metodo Equals() si ottiene un contronto di tipo semantico (e non più meramente matematico).
      • Equals confronta il box (la referenza) e non il contenuto; 
      • L’operatore IS chiama equals al suo interno. 
        • class Program
          {
             static void Main(strings[] args)
             {
                Person p1 = new Person();
                p1.PersonId= 1;
                p1.Name = "Pippo";
          
                Person p2 = new Person();
                p2.PersonId= 1;
                p2.Name = "Pluto";
          
                //Le due istanze avendo lo stesso PersonId, sono semanticamente la stessa
                //anche se per C# le 2 variabili p1 e p2 avendo 2 indirizzi diversi sono diverse
          
                //operator
                Console.Write((i==j)); //false         //confronto matematico
                //polimorfismo OOP
                Console.Write(i.Equals(j)) //true     //confronto semantico
          
             }
          }
          class Person
          {
             public int PersonId {get;set;}
             public string Name {get;set;}
             public int Age {get;set;}
          
             public override bool Equals(object obj)
             {
                 //
                 Person p  = (Person) obj;
                 if(p.PersonId == this.PersonId)
                 {
                    return true;
                 }
                 return false;
             }
          }
          
          ---------------------------------
          object o = 42;
          if(o is 42)   //al suo interno fa => o.Equals(42)
          {
            ...
          }

  • Delegate =>
    • Sintassi generale :
      • [access modifier] delegate <return type> <delegate name>(<parameters>)
    • Definizione :
      • Un delegate è un riferimento ad un metodo.
      • Un delegate definisce un tipo con una particolare signature (parametri in ingresso e di ritorno).
        • Ogni metodo che soddisfa la signatura del delegate può essere assegnato a una variabile di quel tipo.
    • Seppur in modo sicuro implementa i puntatori a funzioni di C++ .
    • Ci sono 3 passaggi quando si lavora con un delegate:
      • Dichiarare un delegate.
      • Collegare un metodo (o più) al delegate.
      • Invocare il delegate.
        • public class Program {
              //passo 1 => dichiarazione delegate
              public delegate string Reverse(string s);
          
              //passo preliminare => implementazione metodo da collegare al delegate
              static string ReverseString(string s)   {
                  return new string(s.Reverse().ToArray());
              }
          
              static void Main(string[] args)  {
                   //passo 2 => collegare il metodo al delegate
                   //è possibile usare anche un metodo anonimo o una lamba expression
                   Reverse rev = ReverseString;
                   
                   //pass 3 => invocare il delegate   
                   Console.WriteLine(rev("a string"));
                   oppure
                   Console.WriteLine(rev.invoke("a string"));
               }
          }
    • Vantaggi  :
      • I delegate sono usati per passare un metodo (con una specifica signature: parametri e valore di ritorno ≠ dal caso di overloading) come parametro ad altri metodi
        • Callback
        • Eventhandler
      • I delegate forniscono una meccanismo di late binding in .NET (significa che il metodo di destinazione viene cercato in fase di esecuzione).
      • I delegate permettono al codice maggiore flessibilità e estendibilità.
        • Sono un’alternativa alle interfacce quando =>
          • sono da preferire quando il chiamante non deve accedere a proprietà o altri metodi nell’oggetto che implementa il metodo.
          • quando si vuole implementare un pattern di progettazione a eventi. 
    • Esempio :
      • Come mostrato qui sotto i delegate permettono di non cablare nel metodo Process quali azioni eseguire, ma aggiungendo una variabile d’ingresso di tipo delegate sarà il client a essere responsabile di aggiungere al delegate le azioni desiderate prima di passarlo al metodo Process.
      • //1- senza delegate
        public void Process(string path)
        {
          var photo = Photo.Load(path);
          var filters = new PhotoFilter();
        
          filters.ApplyContrast(photo);
          filters.ApplyBrightness(photo);
          filters.Resize(photo);
        }
        
        //2- con delegate
        //uso abstract delegate Action per sostiruire => 
        //public delegate void FilterHandler(Photo photo);
        Action<Photo> del1 = = filter.ApplyContrast;
        del1 += filter.ApplyBrightness;
        del1 += filter.Resize;
        Process(path, del1);
        
        public void Process(string path, Action<Photo> filter)
        {
          var photo = Photo.Load(path);
          filter.Invoke(photo);
        }
    • I delegate derivano dalla classe abstract System.Delegate e sono sealed.
    • Multicast delegates  =>
      • E’ possibile passare ad un delegate quanti metodi si vogliono (l’operatore  ‘+’ ci aiuta ad aggiungere un metodo,  l’operatore ‘-’a toglierlo). Ad esempio è possibile associare più metodi ad un singolo evento.
      • Nel caso di signature con parametri di tipo referenza, questi sono visibili da tutti i metodi associati al delegate uno dopo l’altro e le modifiche a questi parametri sono visibile ai metodi successivi.
    • Gestione dei delegate null:  
      • Se non viene attaccato nessun metodo al delegate è possibile incorrere in un NullReferenceException error.
      • Per evitare questo é utile usare l’operatore condizionale null (?.) che permette che non venga eseguito alcun tentativo di registrare il delegate:
      • Esempio =>
        • public static Action<string> WriteMessage;
          WriteMessage?.invoke(msg);   (invece di WriteMessage(msg) che genererebbe un errore)
    • Abstract Delegate
      • Invece di definire un nuovo delegate per ogni nuova funzionalità che vogliamo implementare, è possibile usare dei tipi generici: 
      • Delegate ritorna void:
        • public delegate void Action();
          public delegate void Action<in T>(T arg);
          public delegate void Action<in T1, in T2>(T1 arg1, T2 arg2);
      • Delegate ritorna un valore:
        • public delegate TResult Func<out TResult>();
          public delegate TResult Func<in T1, out TResult>(T1 arg);
          public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
      • Delegate ritorna un boolean (possiamo parlare di predicate):
        • public delegate bool Predicate<in T>(T obj);  (equivale a Func<string, bool> TestForString)
        • esempio predicate =>  
          • //caso1: uso di FindAll tramite lamba expression
            foreach (var libro in libri.FindAll(b => b.Price > 2.5f)) 
            { 
              Console.WriteLine("{0} costa {1} euro", libro.Title, libro.Price); 
            }
          •  //caso2: uso di FindAll tramite il predicate 
            foreach (var libro in libri.FindAll(IsCheap)) 
            { 
              Console.WriteLine("{0} costa {1} euro", libro.Title, libro.Price); 
            }
          • // definizione del predicate 
            static bool IsCheap(Book book) { 
              return book.Price < 3 ? true : false; 
            }
  • Events =>
    • Meccanismo per la comunicazione tra oggetti :
      • Una classe raise (o publisher o event sender) notifica alle classi handle (o subscriber o event receiver) che interagiscono con essa il cambiamento del suo stato
        • Allo scatenarsi di un evento viene quindi invocato il delegate assegnato a tale evento dalla classe handle
      • La classe raise o publisher =>
        • Definisce un delegate (EventHandler or EventHandler<TEventArgs>)
        • Lo espone legato ad un evento (ad es: click).
      • La classe handle o subscriber =>
        • Accetta l’evento.
        • Fornisce un metodo che implementa il delegate:
          1. publicher.click += new EventHandler(subscriber.metodo)
    • Il delegate del publisher finisce per invocare un metodo del subscriber.
      • Quindi è possibile dire che =>
        • Un evento incapsula un delegate
        • Un delegate incapsula un medoto
      • E’ possibile passare ad un evento quanti metodi si vogliono :
        • L’operatore  ‘+’ ci aiuta ad aggiungere un metodo,  l’operatore ‘-’a toglierlo.
      • Bisogna porre attenzione affinché il subscriber rilasci l’accoppiamento con l’evento una volta che questo ha avuto luogo.
    • Vantaggi =>
      • Gli Event forniscono una meccanismo di late binding in .NET (significa che il metodo di destinazione viene cercato in fase di esecuzione).
      • Permettono di progettare applicazioni debolmente accoppiate (loosely coupled).
        • Ogni cambiamento su una classe deve riflettersi il meno possibile sulle altre.
        • E’ possibile estendere un’applicazione aggiungendo funzionalità senza doverne ricompilarne e rideploiarne tutte le componenti.  
      • I delegate permettono al codice maggiore flessibilità e estendibilità.
    • Dichiarare un evento =>
      • Un evento può essere dichiarato in 3 steps:
        1. Dichiarare un tipo delegate per contenere la lista dei metodi da chiamare quando l’evento verrà scatenato. Il delegate dovrà avere la seguente signature:
          • void <OnEventHandlerDelegate>(object sender, EventArgs e)
        2. Dichiarare una variabile del delegate con la chiave event
        3. Dichiarare un metodo per fare il raise dell’evento
          • Esempio :
            • var videoEncoder = new VideoEncoder(); //publisher
              var messageService = new MessageService(); //subscriber1
              var mailService = new MailService(); //subscriber2
              
              //collega all'evento della classe publisher
              //i metodi della classe subscriber che voglio utilizzare
              videoEncoder.VideoEncoded += messageService.Send; 
              videoEncoder.VideoEncoded += mailService.Send;
              videoEncoder.Encode(video);
              
              public class VideoEncoder
              {
              
              //step1 - Declare delegate 
                public delegate void OnVideoEncoderEventHandler(object sender, EventArgs e);
              
               //step2 -  Declare event on that delegate 
                public event OnVideoEncoderEventHandler? VideoEncoded;  
                
              //step3 => Declare method for raising event 
                protected virtual void OnVideoEncoded()    
                {  
                   VideoEncoded?.Invoke(this, EventArgs.empty); 
                }
                
                //method called by main application 
                public void Encode(Video video)
                {
                  ...
                  ...
                  OnVideoEncoded();
                }
              }
      • oppure in 2 steps:
        1. Dichiarare un evento usando il delegate predefinito EventHandler 
        2. Dichiarare un metodo per fare il raise dell’evento
          • Esempio =>
            • //step1 not needed
              //step2 => Declare event on that delegate 
              public event EventHandler<Bool> VideoEncoded; 
              //il resto è identico al caso con 3 steps
              
              
    • Passaggio parametri al subscriber =>
      • Se non ci sono parametri è possibile usare => EventArgs.Empty
      • Esempio con passaggio di un solo parametro (es: boolean) =>
        • //declaring  an  event  using  EventHandler with 1 parameter
          public event EventHandler<Bool> VideoEncoded; 
          protected virtual void OnVideoEncoded(bool IsSuccessful) {
              VideoEncoded?.Invoke(this, IsSuccessful);
          }
          
          //I metodi del subscriber vanno aggiornati per avere la nuova signature
          //void Send(object? sender, bool e)
      • Esempio con passaggio di 2 parametri o più . Bisogna creare una classe che eredita da System.EventArgs e disegnare questa classe affinché possa accogliere i parametri che vogliamo passare al subscriber =>
        • //declaring  an  event  using  EventHandler with multiples parameters
          public event EventHandler<VideoEventArgs> VideoEncoded;
          
          protected virtual void OnVideoEncoded(VideoEventArgs e) {
              VideoEncoded?.Invoke(this, e);
          }
          
          class VideoEventArgs : EventArgs 
          {
              public bool IsSuccessful { get; set; }
              public DateTime CompletionTime { get; set; }
          }
          
          //I metodi del subscriber vanno aggiornati per avere la nuova signature
          //void Send(object? sender, VideoEventArg e)
    • Eventi con subscribers asincroni:
      • Esempio => 
        • worker.StartWorking += async (sender, eventArgs) => {
             try {
                   await DoWorkAsync();
              } catch (Exception e) {
                  //Console.WriteLine($"Async task failure: {e.ToString()}");
              }
          };
  • Expression Tree
    • Un Expression tree is una rappresentazione in-memory di una lambda expression.
    • Permettono di rappresentano del codice in una struttura ad albero, dove ogni nodo é un ‘expression.
      • Rendendo la struttura di una lamba expression trasparente e esplicita.
      • Potendo interagire con la struttura dati di un expression tree come si fa con una qualsiasi struttura dati. 
    • Possono essere compilati in normali delegate (Func<T>)
      • static void CompileAndRunSimpestExpression() 
        { 
            Expression<Func> expression = () => 0; 
        
            //Il medoto .Compile() compila l'Expression Tree in IL (Intermediate Language) 
            //cosi da avere del codice eseguibile e produrre il delegate 
            Func<int> func = expression.Compile(); 
            int result = func();
        }
    • Possono essere costruiti tramite (Expression<TDelegate>):
      • Sintassi lambda expression (più semplice)
      • Sintassi API (permettre di creare Expression dinamiche)
      • static void ConstructSimplestExpression()
        {
            Expression<Func> expression;
        
            expression = () => 0; // Construct expression tree through lambda expression syntax.
        
            expression = Expression.Lambda<Func>(Expression.Constant(0)); // Construct the tree through API syntax.
        }
    • LINQ :
      • Sono molto utilizzati in LINQ dove possono essere utilizzati per mantiene gli elementi di una query e non il risultato.
      • Infatti una “query expression” é compilata in un “expression tree” o in un delegare in rapporto al tipo applicato al risultato di una query:
        • LINQ queries con risultato IEnumerable<T> sono compilate in delegate e eseguite in process.
        • LINQ queries con risultato IQueryable or IQueryable<T> sono compilate in “expression tree” e eseguite out of the process

 

Regular Expression

  • Pattern => [A-Z], [a-z], [0-9], [A-Za-z0-9]
  • (B)rackets 
    • Curly {} => specifico la lunghezza della validazione (es: {1,20}… da 1 a 20 )
      • {n}[26] The preceding item is matched exactly n times.
        {min,}[26] The preceding item is matched min or more times.
        {,max}[26] The preceding item is matched up to max times.
        {min,max}[26] The preceding item is matched at least min times, but not more than max times.
    • Square [] => spefico quali caratteri sto ricercando o non sto ricercando (es: [A-Z]… da A maiuscola a Z maiuscola)
    • Round => specifico un gruppo di valori accettati (es: (com|org|net))
  • (C)arrot (^) => specifica l’inizio della regula expression
  • (D)ollar ($) => specifica la fine della regular expression
  • Boolean or => implemento l’operatore logico or tramite la vertical bar (|)
  • Quantificatori =>
    • ? (0,1) =>  Il ‘punto di domanda’ Indica zero o una occorrenza dell’elemento precedente. Ad esempio, il colou?r corrisponde sia a “color” che a “colour”
    • * (0, molti) => L’ ‘asterisco’ indica zero o più occorrenze dell’elemento precedente. Ad esempio, ab*c corrisponde a “ac”, “abc”, “abbc”, “abbbc” e così via.
    • + (1, molti) => Il ‘segno più’ indica una o più occorrenze dell’elemento precedente. Ad esempio, ab+c corrisponde a “abc”, “abbc”, “abbbc” e così via, ma non a “ac”.
  • Wildcard (Jolly)=>
    • (.) => Il ‘punto’ corrisponde ad ogni carattere. Per esempio , a.b corrisponde ad ogni stringa che contiene ‘a’ e poi qualsiasi carattere e poi ‘b’
  • using System.Text.RegularExpressions;
    
    var pattern = "^[A-Z](1,2)$";
    var stringToCheck = "something";
    
    Regex obj = new RegExp(pattern);
    var isMatching = obj.IsMatch(stringToCheck);
  •  Timeout (> 4.5 .NET) =>
    • E’ possibile dare un timeout di esecuzione ad una regular expression per evitare  DOS (Deny of service) attack.
    • //se l'esecuzione della regex mette più di 2 secondi, si interromperà automaticamente
      var regex = new Regex(@"^(\d+)+$", 
                            RegexOptions.Singleline, 
                            TimeSpan.FromSeconds(2));

 

Programmazione multithread (TPL – Task Parallel Library)

  • Concorrenza (single core):
    • Implementazione del multitasking in un processore single-core.
    • Simula il parallelismo.
    • Il risultato finale è deterministico (noto a priori) mentre l’ordine di processo dei task è indeterministico (può variare da esecuzione a esecuzione).
    • Lo scopo è evitare il blocco dell’applicazione.
    • Un giocoliere è un esempio di concorrenza (deve fare molte operazioni in un tempo che può sovrapporsi).
  • Parallelismo (multi-core):
    • Il programmatore vuole artificialmente dividere una grossa elaborazione (CPU-bound) in più piccole parti
      • Fare più calcoli nello stesso istante => sfruttando il multi-thread o una CPU multi core.
    • Implementazione del multitasking tramite l’utilizzo di un processore multi-core o di più processor.
    • Lo scopo sono le performance.
    • Un call center è un esempio di parallelismo (ogni operatore svolge i propri task indipendentemente e parallelamente agli altri)
  • Operazioni CPU-Bound :
    • Utile nel caso di operazioni CPU-Bound
  • Task :
    • Per implementare il parallelismo è necessario dividere i codice in task e usare la libreria TPL offerta da .NET.
    • E’ necessario che tali task sia indipendenti.
    • E’ necessario che l‘ordine di esecuzione dei task non ne influenzi il risultato.
  • Prevedibilità :
    • Nella programmazione multi-thread non è garantità la prevedibilità del risultato.
  • Parallelismo sui dati  =>
    • Accelera l’esecuizione nel caso si abbia un set di dati e si voglia realizzare la stessa operazione su ognuno di essi (es: filtrare gli elementi di un array)
    • Parallel.ForEach / Parallel.For :
      • Esegui il loop usando un meccanismo multi-thread.
      • L’esecuzione non segue necessariamente la sequenza naturale del loop
      • using System.Threading.Tasks;
        
        var options = new ParallelOptions()
        {
           MaxDegreeOfParallelism = 6
        }
        
        Parallel.For(1, 10, number => { 
                        Console.WriteLine($"value of count = {number}, thread = {Thread.CurrentThread.ManagedThreadId}"); 
                        thread.Sleep(10);
                     });

      • Stop => Interrompi tutte le esecuzioni appena possibile.
      • Break => Interrompe l’esecuzione del loop all’attuale thread ma termina tutte le operazioni legate ai thread precenti.
    • Parallel LINQ.
  • Parellelismo sui task  =>
    • Avviene quando si hanno più task indipendenti e si vogliano eseguirli in parallelo (es: inviare una mail e un sms)
    • Parallel.Invoke :
      • Parallel.Invoke(
             Method1, 
             Method2, 
             Method3,
             //anonymous method call
             () => { ... } ,
             //inline delegate
             delegate () { ... }
        
        );
        
        
        public void Method1()
        {
         ...
        }
        
        public void Method2()
        {
         ...
        }
        
        public void Method3()
        {
         ...
        }
        
        

 

Programmazione asincrona (TAP – Task-based asynchronous programming)

  • Process vs Thread
    • Due istanze di uno stessa applicazione su un computer vengono eseguite da 2 processi diversi e isolati.
    • Il sistema operativo decide come assegnare ai processi attivi le risorse disponibili (1 o più CPU).
    • All’interno di un processo possono esserci molteplici thread e essi possono accedere alle stesse risorse.
  • Obiettivo :
    • Operazioni I/O => Si occupa di migliorare le operazioni relative all’ I/O che coinvolgono più entità.
    • E’ molto usata nelle applicazioni web che per natura sono già multithreading  (1 HTTP Request -> 1 thread)
      • Permette di liberare un thread senza attenderne l’esecuzione per poter gestire una nuova HTTP Request.
      • Permette al UI Thread di non bloccarsi.
  • Come funziona :
    • Una routine chiamata in modo asincrono:
      • Vedrà l’esecuzione del programma che l’ha chiamata continuare alla linea successiva senza attendere che la sua esecuzione sia terminata.
      • Synchronization context
        • Non viene necessariamente creato e utilizzato un secondo thread ma la chiamata asincrona è gestita dal thread principale
        • Ogni task asincrono è eseguito dallo stesso thread.
        • Non tutti i tipi di applicazione pero’ implementano la sincronizzazione del context (es: console application)
          • In tali casi quando l’esecuzione è ripresa dopo che il task asincrono è stato eseguito, il processore non potendo recuperare il context sincrono di partenza crea un nuovo thread
          • Facendo credere che la chiamata asyncrona sia gestita con l’uso di multithreading ma non è così alla base. 

 

  • Vantaggi :
    • Aumenta la responsabilità.
    • In certe condizioni aumento la scalabilità (se altre risorse non fanno da bottleneck).
      • Visto che un thread gestito da IIS (nel caso di web application) può liberarsi più velocemente può essere utilizzato per un’altra richiesta
      • Senza attendere il completamento della prima operazione, sempre che a valle il sistema sia scalabile (es: SQL cluster, Sql Azure, Mongo Db).
    • !Non aumenta le performance!
  • Esempio di utilizzo :
    • Accedere al web (a una rete)
    • Gestire file 
    • Accedere DB
    • Lavorare con immagini
    • Principale tipi .NET con metodi asincroni =>
      • JsonSerializer
      • StreamReader
      • StreamWriter
      • XmlReader
      • XmlWriter
      • HttpClient
      • WCF programming
  • Task-base asyncronous pattern (TAP)  :
    • Approccio classico :
      • Multi-Threading
      • Callback => quando una routine asincrona ha terminato, esegue le linee di codice specificate nella routine di callback.
    • Nuovo approccio TAP (> NET 4.5) :
      • Async :
        • Se marco un metodo con questa parole chiave faccio sapere al compilatore tale routine è asincrona.
        • Il compilatore dovrà: 
          1. Eseguire il codice della routine fino alla chiamata asincrona presente. 
          2. Riprendere subito l’esecuzione dalla prima istruzione dello stack, senza attendere che la chiamata asincrona termini.
          3. Una volta che l’esecuzione della chiamata asincrona è terminata, riprendere autonomamente la routine per eseguirne la parte che sta dopo la chiamata asincrona.
      • return Task  :
        • Un metodo marcato come async restituisce un Task (al posto di void) o Task<classe> (al posto dell’oggetto classe). 
        • Nell’esempio seguente nella variabile getStringTask ci sarà una stringa contenente il risultato del metodo asincrono GetStringAsync solo quando quest’ultimo avrà finito la sua esecuzuone :
          • Task<string> getStringTask = new HttpClient().GetStringAsync(url)
        • Il valore di ritorno di un metodo async dovrà essere:
          • Task<TResult> :
            • Se la versione sincrona del metodo ritorna il tipo <TResult>
            • Il metodo ha un return.
            • In questo caso l’operatore await produrrà un risultato di tipi <TResult>.
          • Task :
            • Se la versione sincrona del metodo ritorna void.
            • In questo caso l’operatore await non produrrà alcuna variabile.
          • Void :
            • Se si sta scrivendo un event handler asincrono.
            • Il chiamante non potrà catturare eventuali exceptions lanciate dal metodo.
          • ValueTask<TResult>
            • Se il metodo sincrono ritorna un tipo valore (es: int)
          • Ogni tipo che un GetAwaiter (da C# 7.0) 
          • IAsyncEnumerable<T> (da C# 8.0)
            • await foreach (var item in ReadWordsFromStreamAsync()) 
              { 
                Console.WriteLine(item); 
              } 
              
              static async IAsyncEnumerable<string> ReadWordsFromStreamAsync() 
              { 
                string data = @"This is a line of text. Here is the second line of text. 
                                And there is one more for good measure. Wait, that was the penultimate line."; 
              
                using var readStream = new StringReader(data); 
                string? line = await readStream.ReadLineAsync(); 
              
                while (line != null) 
                { 
                  foreach (string word in line.Split(' ', StringSplitOptions. RemoveEmptyEntries)) 
                  { 
                     yield return word; 
                  } 
                  line = await readStream.ReadLineAsync(); 
                }
              }
      • Await :
        • Faccio sapere al compilatore di non attendere la fine dell’esecuzione di tale procedura ma rimandare subito l’esecuzione alla pila dello stack.
        • E’ possibile usare la parola chiave await solo in metodi marcati come async.
        • Aggiungere “Async” come suffisso di ogni nome di metodo marcato come async.
        • Se il metodo async restituisce Task<TResult> allora l’operatore await produrrà il risultato “TResult”.
        • Attenzione a usare LINQ quando si lavora con metodi asincroni.
      • private async void Button_Click(object sender, RoutedEventArgs e)
        {
          var getHtmlTask = GetHtmlAsync(@"https://docs.microsoft.com");
          MessageBox.Show("appaio appena ho cliccato");
          var html = await getHtmlTask;
          MessageBox.Show("appaio solo dopo GetHtmlAsync ha terminato"); 
          MessageBox.Show(html.Substring(0, 10));
        }
        
        public async Task<string> GetHtmlAsync(string url)
        {
          var webclient = new WebClient();
          await Task.Delay(3000);
          return await webclient.DownloadStringTaskAsync(url);
        }
    • Attendere il completamento di più attività :
      • Task.WhenAll
      • Task.WhenAny
  • Machine states
    • Async internamente usa gli stati macchina.
  • Modelli asincroni
    • Tipo I/O :
      • Da usare quando il codice da scrivere attende qualcosa (come i dati da un database)
        • private readonly HttpClient _httpClient = new HttpClient();
          
          downloadButton.Clicked += async (o, e) =>
          {
            // This line will yield control to the UI as the request
            // from the web service is happening.
            // The UI thread is now free to perform other work.
            var stringData = await _httpClient.GetStringAsync(URL);
            DoSomethingWithData(stringData);
          };
    • Tipo CPU :
      • da usare qualdo il codice da scrivere deve eseguire una coputazione onerosa
        • private DamageResult CalculateDamageDone()
          {
           // ....
           // Does an expensive calculation and returns
           // the result of that calculation.
          }
          
          calculateButton.Clicked += async (o, e) =>
          {
            // This line will yield control to the UI while CalculateDamageDone()
            // performs its work. The UI thread is free to perform other work.
            var damageResult = await Task.Run(() => CalculateDamageDone());
            DisplayDamage(damageResult);
          };

 

Gestione dei nullable

  • Operatore nullable (?) =>
    • Permette di indicare che la variabile a cui viene appeso può essere null
      • Datetime? birthdate = null;
        //Corrisponde a Nullable<DateTime>
    • Proprietà di una variabile nullable =>
      • Value => ritorna il valore della variazile nullable. Se è null, ritorna un exception.
      • GetValueOrDefault => ritorna il valore della variabile nullable. Se è null, ritorna il valore di default del tipo assegnato alla variabile.
      • HasValue => ritorna true se la variabile nullable ha un valore altrimenti false.
  • Operatore null-coalescing  (??) =>
    • Ritorna il valore dell’operando di sinistra se non è nullo. Altrimenti ritorna l’operando di destra:
      • c = a ?? b
        //c = a se a non è null altrimenti c = b
        //Corrisponde a => c = (a!=null) ? a : b
  • Operatore null-coalescing assignement  (??=) => 
    • Assegna il valore dell’operando di destra all’operando di sinistra solo se tale operando è null:
      • c ??= "prova" 
        //c assume il valore "prova" solo se c era null
  • Nullable refecence context
    • Nullable refence types (C# 8 or later)
      • Per attivare i nullable reference types  andare nel file csproj (a livello di progetto o di file singolo)  
        • <pre>
            <Project Sdk="Microsoft.NET.Sdk"> 
              <PropertyGroup> 
               <OutputType>Exe</OutputType>   
               <TargetFramework>netcoreapp3.0</TargetFramework> 
               <LangVersion>8.0</LangVersion> 
               <Nullable>enable</Nullable> 
              </PropertyGroup> 
            </Project>
          </pre>
    • Operatore Null-forgiving (!) => (C# 8 or later) 
      • In un ambiente con il nullable context attivo, se il programmatore sa che la variabile data non sarà mai null ma il compiler restituisce un null warning allora è sufficiente aggiungere l’operatore ! alla variabile per sovrascrivere tale comportamento:
        • name!.lenght
    • Attributi per l’analisi statica del null-state (da parte del compilatore C#) 
      • [AllowNull] (precondition):  un argomento non-nullable può essere null. Anche se di norma il valore è non nullo, questo attributo specifica che può essere nullo 
      • [DisallowNul] (precondition): un argomento nullable non dovrebbe mai essere nullo. Anche se di norma il valore è nullable, questo attributo specifica che non si accettano valori nulli.
      • [return: MaybeNull] (postcondiction):  questo attributo indica che una variabile non-nullable ritornata ad esempio da un metodo potrebbe essere il valore null.
      • [NotNull] (postcondiction): anche se la variabile tornata può essere nulla, questo attributo indica che non si accettano valori nulli.
      • [MaybeNullWhen(true)] or [MaybeNullWhen(false)] (conditional post-conditions): questi attributi indicano che un argomento di solito non-nullable può essere nullo nel caso in cui il metodo ritorni true (o false)
      • [NotNullWhen(true)] or [NorNullWhen(false)] (conditional post-conditions): questi attributi indicano che un argomento di solito nullable non sarà nullo nel caso il metodo ritorni true (o false) 
      • [NotNullIfNotNull] (conditional post-conditions):  questo attributo indica ad esempio che un valore di ritorno di un metodo non sarà mai nullo se l’argomento (in ingresso al metodo) dello specifico parametro non è nullo
      • [MemberNotNull] (helpers method):  indica che le proprietà elencate come parametro di questo attributo non possono assumere valori nulli all’interno del metodo helper associato al costruttore della nostra classe.
      • [MemberNotNullWhen(returnValue: true, member:nameof(Value))]  (helpers method): se la proprieta ritorna true allora la variabile Value è sicuramente non nulla.
      • [DoesNotReturn] (flow attribute):  permette di indicare al compiler che nessuna analisi dei valori nulli é necessaria a partire da quel punto o per quel dato metodo
      • [DoesNotReturnif[true)] or [DoesNotReturnif[false)] (flow attribute):  permette di indicare al compiler che nessuna analisi dei valori nulli é necessaria per un dato metodo se questo ritorna true (o false)

 

Custom snippet

  • Per aggiungere uno snippet
    • Creare un file .snippet
    • Aprire C# project => Tools => Code Snippet Manager => My Code Snippets => Import
    • Il file verrà importato => C:\Users\richlab\Documents\Visual Studio 2022\Code Snippets\Visual C#\My Code Snippets\..
  • Esempi file .snippet
    • 1- snippet 'utestm'
      //Stiamo creando uno snipper che verrà richiamato tramite la parola chiave <utestm>
      //E' possibile definire dei parametri che verranno richiesti
      //per customizzare lo snippet richiamato
      <?xml version="1.0" encoding="utf-8"?>
      <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
         <CodeSnippet Format="1.0.0">
            <Header>
               <Title>NUnit.TestMethod</Title>
               <Author>Myself</Author>
               <Description>Prompt a void NUnit.Method</Description>
               <Shortcut>utestm</Shortcut>
            </Header>
            <Snippet>
               <Code Language="CSharp">
                    <![CDATA[[Test]
                         public void $Method$_$Scenario$_$ReturnValue$()
                        {
                        }
                    ]]>
               </Code>
               <Declarations>
                  <Literal>
                     <ID>Method</ID>
                     <ToolTip>Choose a method name.</ToolTip>
                     <Default>Method</Default>
                  </Literal>
                  <Literal>
                      <ID>Scenario</ID>
                      <ToolTip>Choose a Scenario name.</ToolTip>
                      <Default>Scenario</Default>
                  </Literal>
                  <Literal>
                      <ID>ReturnValue</ID>
                      <ToolTip>Choose a Return value.</ToolTip>
                      <Default>ReturnValue</Default>
                  </Literal>
               </Declarations>
            </Snippet>
         </CodeSnippet>
      </CodeSnippets>
      
      2- snippet shortcut 'gs'
      //Creare la stringa {get;set;}
      
      <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
        <CodeSnippet Format="1.0.0">
          <Header>
            <Title>GetSet</Title>
            <Description>Inserts getter/setter shorthand code</Description>
            <Shortcut>gs</Shortcut>
          </Header>
          <Snippet>
            <Code Language="CSharp">
              <![CDATA[{ get; set; }$end$]]>
            </Code>
          </Snippet>
        </CodeSnippet>
      </CodeSnippets>

 

Linq (Language Integrated Query) =>

  • Fornisce la capacità nativa di interrogare vari tipi di oggetti =>

  • Linq Distinc, Except, Union, Intersect methods:

  • Sintassi (Tramite gli Extension Method oppure Query Operators) =>
    • Where => Per filtrare
    • Select => Per selezionare i parametri/campi dell’output (anonymous object)
    • SelectMany => Da usare quando l’output è una lista di oggetti lista. Permette di evitare il doppio ciclo ma dà accesso diretto ai valori del secondo ciclo.
    • Distinct => Simile al comando distinct in SQL. Permette di visualizzare solo i valori distinti di una certa lista.
    • OrderBy , OrderByDescending, ThenBy, ThenByDescending => per ordinare gli elementi della collezione in output
      • foreach (var item in books
                      .Where(book => book.Price < 5)
                      .OrderByDescending(book => book.Title)
                      .ThenBy(book => book.Price)
                      .Select(b => b.Title))
        {
            Console.WriteLine(item.Title + ' ' + item.Price);
        }
    • Element operator =>
      • Single , SingleOrDefault => Per ritornare un solo oggetto (o null se si usa SingleOrDefault)
      • First, FirstOrDefault => Per ritornare il primo oggetto che soddisfa la query.
        • //cerco il primo libro che costi più di 10 euro
          context.Books.FirstOrDefault(b => b.Price > 10);
      • Last, LastOrDefault => Per ritornare l’ultimo oggetto che soddisfa la query
        • !! non funziona con LINQ to Entities !! => bisogna usare OrderByDescending e poi usare First
    • Paging / Partitioning =>
      • Skip<int> => per saltare un dato numero di oggetti prima di applicare la query.
      • Take<int> => per ritornare solo il dato numero di oggetti.
    • Operatori di quantità =>
      • All, Any => 
        • //ritorna true (o false) se tutti gli oggetti della collazione soddisfano
          //(o meno) il criterio specificato
          var allAbove10Euro = context.Books.All(b => b.price > 10);
          
          //ritorna true (o false) se almeno un oggetto della collazione soddisfa (o //meno) il criterio specificato
          var existAbove10Euro = context.Books.Any(b => b.price > 10);
          
          
    • Operatori di aggegrazione =>
      • Count  => Conta il numero di oggetti che soddisfa la query
        • int contaLibri= books.Where(b => b.Price > 3).Count();
      • Sum<predicate>, Average<predicate) => Ritorna la somma (o la media) dei valori presenti nella collazione in funzione del predicato indicato:
        • float SommaPrezzi = books.Sum(b => b.price)
      • Max<predicate>, Min<predicate> => Ritorna il maggiore (o minore) dei valori presenti nella collazione in funzione del predicato indicato:
        • float PrezzoMaggiore = books.Max(b => b.price)
          float PrezzoMinore = books.Min(b => b.price)
    • Group By  => 
      • //E' diverso dal comando SQL.. Divide semplicemente il risultato 
        //in gruppi rispetto ai campi indicati nella group by
        var videoGruppi = from c in context.Videos
                          group c by c.GenreId
                           into g
                          select g;
        
        foreach (var group in videoGruppi)
        {
              Console.WriteLine("{0} ({1})", group.Key, group.Count());
              foreach (var item in group)
              {
                  Console.WriteLine(item.Name);
              }
        }
        
        var classGroup = context.Videos
             .GroupBy(v => v.Classification)
             .Select(gr => new 
             { 
                  Classification = gr.Key.ToString(), 
                  Videos = gr.OrderBy(v => v.Name) 
             });
    • Inner Join =>
      • //Qualore esista una navigation proterty tra 2 entità non è necessario
        //usare il comando join
        var innerJoin = from v in context.Videos
                        join g in context.Genres
                          on v.GenreId equals g.Id
                      select new {VideoName = v.Name, Genere = g.Name} ;
        
        var innerJoin = context.Videos.Join(
                           context.Genres, 
                           v => v.GenreId,
                           g => g.Id,
                           (video, genre) => new
                              {
                                 VideoName = video.Name, 
                                 Genere = genre.Name
                              });
    • Group Join =>
      • //Questo comando è equivalente alla LEFT JOIN con GROUP BY in SQL
        var groupJoin = from g in context.Genres
                        join v in context.Videos
                          on g.Id equals v.GenreId 
                        into a
                      select new { Genere = g.Name, ContaVideo = a.Count() };
        
        var groupJoin = context.Genres.GroupJoin(
                          context.Videos,
                          g => g.Id,
                          v => v.GenreId,
                         (genre, videos) => new
                         {
                            Genere = genre.Name,
                            Videos = videos.Count()
                         });
        
    • Cross Join =>
      • //Equivale allo comando CROSS JOIN in SQL. 
        //Il risultato è la full combination delle 2 entità
        var crossJoin =  from g in context.Genres
                         from v in context.Videos
                        select new { Genere = g.Name, VideoName = v.Name };
        
        var crossJoin = context.Genres.SelectMany(
                           g => context.Courses,
                           (genre, video) => new
                           {
                             Genere = genre.Name, VideoName = v.Name
                           });

 

Caching

  • Redis =>
    • open source in-memory cache
    • Installare il redis server (dal loro sito)
    • Usare i comandi CLI (command-line interface) =>

    • Installare il client per c# (da nuget)
      • class Program
        {
           static void Main(string[] args)
           {
               ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost");
               IDatabase db = redis.GetDatabase();
               var val = db.StringGet("k1"); 
           }
        }

 

Exception Handling

  • Le Exception sono degli eventi, cattivi eventi. 
  • Exception sono gestite tramite classe
  • Stack trace tiene traccia dei metodi chiamati fino a che l’exception è accaduta.
  • InnerException => contiene un’eventuale eccezione più specifica occorsa prima del blocco più generico che ha gestito l’attuale eccezione.
  • Try…Catch =>
    • E’ preferibile gestire gli errori e la loro visualizzazione per evitare che l’utente si trovi faccia ai messaggi diretti inviati da .NET
    • Se si vuole catturare diversi tipi di eccezioni, bisogna partire dalla più specifica e andare verso la più generica;
    • class Program
      
         static void Main(string[] args)   
         {
            try
            {
               ...
            }
            catch (DivideByZeroException)
            {
               //faccio il catch dell' eccezione più specifica
               ...
            }
            catch (ArithmeticException)
            {
               ...
            }
            catch (Exception)
            {
               //faccio il catch dell' eccezione più generica
               ...
            }
            finally
            {
               //questa parte di codice è sempre eseguita
               //fare qui i metodi dispose delle risorse unmanaged
            }
         }
      
      }
  • Classe Exception => 
    • Ex.StackTrace => mostra il tracing dello stack degli errori (tutta la catena di propagazione dell’errore dalla sorgente al chiamante principale)
    • Ex.Message
    • Ex.InnerMessage
  • Exception progatation =>
    • Le eccezioni sono propagate dal chiamato (dove l’errore è occorso) al chiamante fino a risalire al chiamante principale

  • Custom exception =>
    • public class CustomException : Exception
      {
         public CustomException(string message, Exception innerException) : base(message, innerException)
         {
            
         }
      }
  • Throw vs Throw Ex =>
    • Throw => mantiene lo stack trace completamente
      • try
        {
        }
        catch (Exception)
        {
          throw;
        }
    • Throw ex => risetta lo stack trace
  • Exception swallowing =>
    • Il livelli intermedi posti tra la sorgente dell’errore e il chiamante principale dovrebbero evitare di sovrascrivere l’errore più interno perdendolo.
    • E’ più opportuno che i livelli intermedi propaghino gli errori più interni
      • //intermediate method
        try
        {
        }
        catch(Exception ex)
        {
           throw new Exception("my message" + ex.message);
           
        oppure
        
           throw ex;
        }

 

Preproccessor

  • Preprocessor =>
    • E’ possibile scrivere delle istruzioni per definire come il compiler includerà o escluderà del codice durante la compilazione
    • Golden rules =>
      • Tutti i comandi comincia con #
      • I comandi del preprocessore devono essere messi per primi nella pagina
  • Conditional execution =>
    • Decido di eseguire del codice direttamente nel codice in funzione per esempio di alcune variabili d’ambiente configurate nel file config
  • Conditional compilation =>
    • Decido di compilare o meno del codice tramite le direttive preprocessor
    • #define env32
      using System;
      
      class Program
      {
         static void Main(string[] args)
         {
           #if(env32)
           {
           }
           #else
           {
           }
         } 
      }
  • Tip 1 =>
    • Definire le variabili direttamente dal menù Proprietà => Build => Conditional compilation symbol e non nel codice (come nell’esempio sopra).
    • Inserire nel form le variabili desiderate divise da virgola (,)
    • DEBUG, TRACE => sono istruzioni già definite di default
      • #if (DEBUG)
        {
          ...
        }
        #endif
  • Tip 2 =>
    • Usare #undef syntax per disattivare le variabili inserite dalla pagina Build di Visual Studio
    • Da mettere all’inizio della pagina
  • Tip 3 =>
    • Approfittare dei colori usati da Visual Studio
  • Tip 4 =>
    • #errors e #warning per controllare che non siano attivati Preprocessor non desiderati
  • Tip 5 =>
    • Usare l’attributo [Conditional] per eseguire o meno parti del codice in funzione delle variabili preprocessor
    • #undef env32 
      using System; 
      using System.Diagnostics;
      
      class Program
      {
        static void Main(string[] args)
        {
           Metodo1();  ///il compilatore non invocherà questo metodo perché env32 non è definita
        }
      }
      
      //Eseguo il metodo1 solo se la variabile preprocesso env32 è definita e attiva
      [Conditional("env32")]
      public static void Metodo1
      {
      }
  • Usare il ‘configuration manager’ di Visual Studio per definire delle condizioni diverse per ambienti a 32 o 64 bit

 

Automapper

  • Esempio:
    1- Installare Automapper
    //Visual Studio
    Install-package AutoMapper                   
    
    //Visual Studio Code
    dotnet add package AutoMapper
    dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
    
    2- Configurare Automapper
    //>= .NET 6
    //Program.cs
    builder.Services.AddAutoMapper(typeof(Program));
    
    oppure
    
    // .NET Core
    //Startup.cs
    public void ConfigureServices(IServiceCollection services) {
         // Auto Mapper Configurations
         var mapperConfig = new MapperConfiguration(mc =>
         {
             mc.AddProfile(new MappingProfile());
         });
    
         IMapper mapper = mapperConfig.CreateMapper();
         services.AddSingleton(mapper);
    
         services.AddMvc();
    }
    
    oppure
    
    //ASP.NET MVC 5
    //passo 0B => aggiungo AutoMapper nel Global.axa.cs 
    protected void Application_Start() { 
       ... 
       Automapper.Mapper.Initialize(cfg => cfg.AddProfile<MappingProfile>()); 
    } 
    // passo 1 => Inizializzo AutoMapper con il profile appena creato 
    Mapper.Initialize(cfg => cfg.AddProfile()); 
    
    
    3- Creare oggetti DTO/Resource per mappare il nostro model domain
    class Employee
    {
       public string Name { get; set;}
       public int Salary { get; set;}
       public string Adress { get; set;} 
       public string Department { get; set;} 
    }
    class EmployeeDTO
    {
       public string FullName { get; set;}
       public int Salary { get; set;}
       public string Adress { get; set;} 
       public string Dept { get; set;} 
    }
    
    4- Usare Automapper
    //MODO1 => Usare automapper direttamente nel codice dove ne hai bisogno
    var config = new MapperConfiguration(c => 
    { 
       //GET
       c.CreateMap<Employee, EmployeeDto>()
          .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name)) 
          .ForMember(dest => dest.Dept,     opt => opt.MapFrom(src => src.Department));
        //POST, PUT
        c.CreateMap<EmployeeDto, Employee>() 
          .ForMember(dest => dest.Id, opt => opt.Ingnore());  //ignore primary key
          .ForMember(dest => dest.Name, opt => opt.MapFrom(src => src.FullName)) 
          .ForMember(dest => dest.Department, opt => opt.MapFrom(src => src.Dept))
    }); 
    var mapper = new Mapper(config);
    var employeeDTO = mapper.Map<EmployeeDTO>(employee);
    oppure mapper.Map(employee, employeeDTO2);
     
    [versione corta
    var mapper = new MapperConfiguration(c => c.CreateMap<Customer, Customer>()).CreateMapper();
    var employeeDTO = mapper.Map<EmployeeDTO>(employee);]
    
    //MODO2 => Usare AutoMapper via file di configurazione 
    //passo 0A => file ./Mappings/MappingProfile.cs
    //creo una classe che eredita da AutoMapper.Profile 
    //dove accentrare tutte mappature 
    using AutoMapper;
    public class MappingProfile: Profile
    {
       public MappingProfile()
       {
          CreateMap<Employee, EmployeeDTO>()
            .ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.Dept, opt => opt.MapFrom(src => src.Department));
       } 
    } 
    
    //uso Automapper 
    using Automapper;
    private readonly IMapper _mapper;
    
    public class MyController(IMapper mapper)
    {
       _mapper = mapper;
       var _mappedUser = _mapper.Map<User>(user);
    }

 

Serialization in NET

  • System.Text.Json =>
    • A partire dagli anni 2020 è diventato meglio della libreria NewtonSoft.Json 
    • using System.Text.Json;
      
      var file = await File.ReadAllTextAsync("data.json");
      var cars = JsonSerializer.Deserialize<CarData>(file);
      
      class CarData
      {
         [JsonPropertyName("id")]
         public int ID { get; set; }
      
         [JsonPropertyName("car_make")]
         public string Make { get; set; }
      
         [JsonPropertyName("car_model")]
         public string Model { get; set; }
      }
      
  •  NewtonSoft.Json =>
    • esempio:
      Product product = new Product();
      
      product.Name = "Apple";
      product.ExpiryDate = new DateTime(2008, 12, 28);
      product.Price = 3.99M;
      product.Sizes = new string[] { "Small", "Medium", "Large" };
      
      string output = JsonConvert.SerializeObject(product);
      {
        "Name": "Apple",
        "ExpiryDate": "2008-12-28T00:00:00",
        "Price": 3.99,
        "Sizes": [
          "Small",
          "Medium",
          "Large"
        ]
      };
      
      Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);
  • [JsonIgnore]
    • Dipendenza circolare
      • Durante la scrittura di API è possibile incorrere nel seguente errore
        • JsonException: A possible object cycle was detected which is not supported.
      • Tale errore è dovuto al fatto che si sta cercando di serializzare/deserializzare un oggetto che punta ad un altro oggeto. Il quale a sua volta punta al primo oggetto.
      • Esiste tra 2 oggetti una dipendenza circolare  
    • Come ignorarla:
      • Aggiungere l’attributo [JSonIgnore] al campo o campi che sono origine della dipendenza circolare
    • Come evitararla:
      • Una seconda soluzione sarebbe usare oggetti DTO

 

OData (Open Data Protocol)

  • Cosa é =>
    • E’ uno standard ISO.
    • Rispetta i principi di progettazione RESTFul .
    • OData permette di creare dei servizi interrogabili (queryable) così da poter filtrare i dati che dal web server vanno al client.
    • Rende possibile specificare in modo semplice come si vogliono ordinare, filtrare e selezionare i dati esposti da una API. 
    • Utilizatto da 
      • Microsoft
      • SAP
    • Alternative sul mercato
      • GraphQL (Facebook)
    • Simile a SQL
    • Lavora con HTTP
    • Platform-neutral
    • Metadata
      • Offre dei metadata che spieghino quali sono i dati che possono essere letti tramite ODATA
      • https://api.myserver.com/odata/$metadata
    • Permettono di leggere e di scrivere dati
  • Pros =>
    • Evita di dover scrivere un sacco di end-point specifici
  • Comandi di filtro:
    • $metadata
    • $count
    • $orderby: Sorts the fetched record in particular order like ascending or descending
    • $select: Selects the columns or properties in the result set. Specifies which all attributes or properties to include in the fetched result
    • $skip: Used to skip the number of records or results. For example, I want to skip first 100 records from the database while fetching complete table data, then I can make use of $skip
    • $top: Fetches only top n records. For e.g. I want to fetch top 10 records from the database, then my particular service should be OData enabled to support $top query option.
    • $expand: Expands the related domain entities of the fetched entities.

    • $filter: Filters the result set based on certain conditions, it is like where clause of LINQ. For e.g. I want to fetch the records of 50 students who have scored more than 90% marks, and then I can make use of this query option.
    • $inlinecount: This query option is mostly used for pagination at client side. It tells the count of total entities fetched from the server to the client.
  • Operatori di filtro:
    • eq: equal              ($filter=revenue eq 100)
    • ne: not equal       ($filter=revenue ne 100)
    • gt: greater than   ($filter=revenue gt 100)
    • ge: greater than or equal   ($filter=revenue ge 100)
    • lt: less than                          ($filter=revenue lt 100)
    • le: Less than or equal         ($filter=revenue le 100)
    • and: logical and                 ($filter=revenue lt 100 and revenue gt 200)
    • or: logical or                       ($filter=endswith(name,'(e)’) or startswith(name,’t’))
    • not: logical negation        ($filter=not contains(name,’sample’))

  • Come usare ODATA
    • Installare il package
      • Microsoft.AspNetCore.OData
    • program.cs

    • Controller
      • Il metodo Get che restituisce tutti i Customers non espone in realtà un ToList (ciò non interroga direttamente il DB) come d’abitudine ma ritorna un queryable, una referenza a EF.
      • Quando l’utente interrogherà il risultato con la sintassi OData, questa verrà tradotta in EF e poi in SQL Statement sul DB.

      • https://localhost.7029/v1/Customers
  • Esempi
    • /People('Pippone')            [Restituisce l'oggetto person con <name>='Pippone', dove <name> è la PK]
      
      /Airports('BGY')/Name         [Restituisce il Name dell'aeroporto con <codice>='BGY', <codice> è la PK]
      
      /People?$filter=FirstName eq 'Scott' 
      
      /People/$count                [Restituisce il numero di People presenti]

 

OWIN (Open Web Interface for .NET) =>

  • E’ una specifica open-source che descrive un livello di astrazione tra web servers e le applicazioni.
  • Lo scopo è dare una struttura modulare alle applicazioni web.NET incoraggiando la scrittura di componenti (middlewares) che possono essere uniti in una pipeline attraverso la quale il server può instradare le richieste che riceve.
  • Superare la concorrenza di altri framework open source come NodeJs, Ruby.
  • Cerca di rimuove la dipendenza delle applicazioni Asp.Net dall’assembly system.Web risulta fortemente accoppiato con il sistema operativo Windows.
  • public AppFunc SomeMiddleware(AppFunc next)
    {
        // Create and AppFunc using a Lambda expression:
        AppFunc appFunc = async (IDictionary<string, object> environment) =>
        {
            // Do some processing ("inbound")...
            // Invoke and await the next middleware component:
            await next.Invoke(environment);
     
            // Do additional processing ("outbound")
        };
        return appFunc;
    }
    

     

  • KATANA
    E’ un set di componenti open-source sviluppati da Microsoft per la realizzazione e l’hosting di applicazioni web basate sulle speficiche OWIN.
    Fornisce quindi un’implementazione delle specifiche OWIN e una serie di componenti middleware prondi all’uso.

 

Threading (Parallel code execution)

  • Foreground Thread (default) =>  continuano l’esecuzione anche se il Thread genitore è finito.
  • Background Thread => si interrompono quando il Thread genitore finisce.
  • using System.Threading;
    
    class Program
    {
       static void Main(string[] args)
       {
           //created 2 threads
           Thread obj1 = new Thread(Function1);
           obj1.IsBackground = true;
           Thread obj2 = new Thread(Function2);
    
           //started all threads
           obj1.Start();
           obj2.Start();
       }
    
       static void Function1() {
          ...
       }
    
       static void Function2() {
          ...
       }
    }
  • Trade safety => Nessun altro thread può accedere ad un safe code mentre un primo thread vi ci accede.

    • Lock / Monitor  (gestiscono solo in-memory thread) =>
      • //il codice inserito nello statement lock sarà eseguito da un solo thread alla volta
        //ciò evita problemi nell'esecuzione multi-threading
        public void divide() 
        {
            //lock è un syntactic sugar di Monitor
            lock(this)
            {
                ....
               ....
            }
        }
        
        oppure
        
        public void divide() 
        { 
             Monitor.Enter(this);
             .... 
             .... 
             Monitor.Exit(this);  
        }
    • Mutex
    • Semaphore
    • SemaphoreSlim

 

Collection (Array, ArrayList, Hashtable, Stack / Queue, Specialized collections, Generic collections)

  • Array (Indexed based) :
    • - Fixed sized
      - Strongly typed objects
      - Access data only by index
      - Better performances than ArrayList();
      
      public class MyArray
      {
         string[] str = new string[10];
         int[] i;
         
         public MyArray()
         {
              for(int i=0;i < str.Lenght; i++)
              {
                string[i] = string.contact('item', i);
              }
         }
      }
  • ArrayList (Indexed based) :
    • - Resizable collection
      - Generic collection 
      - Access data only by index
      
      using System.Collectins;
      public MyArrayList
      {
          ArrayList al = new ArrayList();
             
           public class MyArrayList()
           {
              al.Add("item1");
              al.Add("item2");
           }
      }
  • Hashtable (key value pair) :
    • - Permettono di accedere ai dati tramite una chiave (e non solo un indice)
      - Nella programmazione di tutti giorni questo è un importante requisito
      - Converte le stringhe usate come chiave in hash (interger value)
        e usa tali hash valore per memorizzare i dati nelle varie posizioni della hasttable 
      - Sono più lenti degli ArrayList ma hanno la possibilità di lookup (ricerca per chiave)
      
      public class MyHashtable
      {
         HastTable customers = new HastTable();
         Customer c1 = new Customer();
         Customer c2 = new Customer();
      
         customers.Add("code1", c1);
         customers.Add("code2", c2);
      
         public MyHashtable()
         {
            c1 = (Customer) customers["code1"];
         }
      }
  • Stack / Queue (prioritized collection) =>
    • FIFO => queue
      LIFO => stack
      
      using System.Collections;
      
      public class MyQueue
      {
         Queue q = new Queue();
      
         public MyQueue
         {
             //accodo
             q.Enqueue("item1");
             q.Enqueue("item2");  
      
             //scodo
             var item1 = q.Dequeue();
             var item2 = q.Dequeue();
         }
      }
      
      public class MyStack
      {
         Stack s = new Stack();
      
         public MyStack
         {
            //inserisco nella pila
            q.Push("item1");
            q.Push("item2"); 
      
            //tolgo dalla pila
            var item2 = q.Pop();
            var item1 = q.Pop();
         }
      }
  • Specialized collections (preferire generic collectons) =>
    • - non molto usate
      
      using System.Collections.Specialized;
      
      //1- CollectionsUtil
      HastTable at = CollectionsUtil.CreateCaseInsensitiveHashtable
      at.Add("shiv", Shiv);
      
      Shiv shiv = (shiv)at["SHIV"];
      
      
      //2- ListDictionary  (use for < 10 elements collections)
      ListDictionary ld = new ListDictionary();
      
      //3- Hybrid dictionary 
      - permette di usare con una combinazione di ListDictonary e Hashtable
      - utile per > 10 elements collections
      HybridDictionary hd = new HybridDictionary();
      hd.Add("dict1", dict1);
      hd.Add("hast1", hashTable1);
      
      //4- String collection
      - permette di memorizzare string
      
      StringCollection sc = new StringCollection();
      sc.Add("item1", "messaggio1");
  • Generic collections (più usati) =>
    • - strong typed come Array
      - flexible come ArrayList e HashTable
      - separano la logica del tipo di dato (che sarà deciso solo a runtime)
         - Add
         - Remove
         - Clear
         - Search 
      - aumenta la riusabilità infatti la logica può essere applicata a ogni tipo di dato
      
      Ognuno dei metodi visti prima ha la sua versione generic =>
      - ArrayList => List<T>();
      - HashTable => Dictionary<string, string>();
      - Stack and Queue => Generic Stack<T> and Queue<T>
      
      using System.Collection.Generic;
      
      public class MyGenericCollection
      {
          public MyGenericCollection
          {
              Check<int> objInt = new Check<int>();
              bool b1 = objInt.Compare(1,2);
              
              Check<string> objString = new Check<string>();
              bool b2 = objString.Compare("1","2");
      
               //List
               List<int> obj = new List<int>();
               obj.Add(123);
      
               List<string> obj = new List<string>();
               obj.Add("code1");
      
               //Dictionary
               Dictionary<string, string> objDict = new Dictionary<string, string();
               objDict.Add("code1", "oggetto1");
      
               //Stack / Queue
               Stack<int> stack = new Stack<int>();
               Queue<string> queue = new Queue<string>();
          }  
      }
      
      public class Check<T>
      {
          public bool Compare(T i, T y)
          {
             if(i.Equals(y))
                 return true;
      
             return false;
          }   
      }
  • Generic constraint =>
    • Posso limitare i tipi applicabili alla mia classe generic
      
      public class Check<T> : where T : class
      { 
          public bool Compare(T i, T y) 
          { 
               if(i.Equals(y)) 
                  return true; 
            
               return false; 
          }
      }
  • IComparable vs IComparer =>
    • IComparable :
      • Permette di definire una logica custom di ordinamento in una collezione di oggetti invocabile tramite il meotdo Sort();
      • es: Voglio ordinare per età e nel caso di età uguali ordinare per nome.
      • class Program()
        {
           List<Person> people = new List<Person>();
        
           people.Add(Person1);
           people.Add(Person2);
        
           //il metodo Sort() per poter funzionare deve appoggiarsi sul metodo 'CompareTo' da definire in 'Person'
           people.Sort();
        }
        
        1- Implementare nella classe Person l'interfaccia IComparable
        class Person : IComparable<Person>
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Age {get; set;}
        
            //implementare questo metodo per implementare l'interfaccia IComparable
            public int CompareTo(Person other)
            { 
               //definire qui la logica di ordinamento desiderata
               
               //ordinare per FirstName, se le età sono uguali
               if(this.Age == other.Age)
               {
                  return this.FistName.CompareTo(other.FirstName);
               } 
               
               //ordino per età
               return this.Age.CompareTo(other.Age);
            }
        }
    • IComparer :
      • Permette di decidere un ordinamento condizionale da indicare al momento dell’invocazione del metodo Sort();
      • es: Voglio poter decidere se ordinare per nome e altre per età.
      • class Program
        {
           List<Person> people = new List<Person>();
        
           people.Add(Person1);
           people.Add(Person2);
        
            //il metodo Sort() per poter funzionare deve appoggiarsi sul metodo 'CompareTo' da definire in 'Person'
            //decido di lanciare il metodo Sort() indicandogli di usare la classe CompareByName per ordinare la 
            //collezione per nome
            people.Sort(new CompareByName());
        
            oppure
        
            //in questo secondo caso usando la classe CompareToAge il metodo Sort() ordinerà la collezione per età
            people.Sort(new CompareByAge());
        }
        
        1- La classe Person non necessita di implementare IComparable
        class People
        {
           public string FirstName { get; set; } 
           public string LastName { get; set; } 
           public int Age {get; set;} 
        }
        
        2- In compenso definire 2 o più versione della mia classe in funzione di quali ordinamenti si vogliono usare
        class CompareByName : IComparer<Person>
        {
            public int Compare(Person x, Person y)
            {
                return string.Compare(x.FirstName, y.FirstName);
            }
        }
        
        class CompareByAge: IComparer<Person>
        {
            public int Compare(Person x, Person y)
            {
                 //Equal => 0
                 if(x.Age == y.Age) return 0;
        
                 //Greater => 1
                 if(x.Age > y.Age) return 1;
        
                 //Less => -1 
                 if(x.Age < y.Age) return -1;
            }
        }
  • IEnumerable vs IEnumerator =>
    • IEnumerable e IEnumerator sono entrambe interfacce.
    • IEnumerable :
      • E’ una cosa che può essere elencata.
      • Ha un’impostazione di tipo pull-based :
        • I valori nella collezione sono generati solo dopo che sono stati effettivamente richiesti (per esempio facendo .ToList o un foreach sulla collezione).
        • esecuzione differita
      • Ha un solo metodo chiamato GetEnumerator.
        • Questo metodo restituisce un altro tipo che è un’interfaccia e quell’interfaccia è IEnumerator.
      • IEnumerable è come una scatola che contiene IEnumerator al suo interno (sebbene non per ereditarietà o contenimento).
      • IEnumerable
        
        //espone un solo metodo
        public IEnumerator GetEnumerator();
    • IEnumerator :
      • E’ una cosa che può elencare.
      • Ha 2 metodi (MoveNext e Reset) e una proprietà Current.
      • L’ultimo MoveNext (all’utlimo elemento delle collezione) ritorna FALSE .
      • IEnumerator
        
        public object Current;
        public void Reset();
        public bool MoveNext();
  • IEnumerable vs IQueryable =>
    • IEnumerable :
      • Rappresenta ogni oggetto che può essere enumerato (iterato con un foreach block)
        • string, array, list, dictionary. 
      • L’enumerazione è in sola lettura e solo in avanti.
      • Non contiene i metodi Count() (è necessario iterare sulla collezione per sapere quanti oggetti contiene),  Add(), Remove().
      • Supporta il filtering degli elementi che lo compongono usando l’estension method Where ma non in modo differito. 
        • I filtri vengono applicati ai dati una volta caricati in memoria (vs IQueryable).
        • La logica del filtro è eseguita in-memory.
      • Gli extension methods per un IEnumerable operano con Func<T>.
    • ICollection :
      • Estende IEnumerable introducendo i metodi Count(), Add(), Remove(), Update().
    • IList :
      • Dstende ICollection introducendo la possibilità di inserire o eliminare elmenti nel mezzo della lista.
    • IQueryable :
      • Deriva da IEnumerable (ogni cosa che IQueryable è anche IEnumerable).
      • Permette l’esecuzione differita (un’ oggetto di tipo query può essere esteso senza che venga eseguito alcun comando sul DB).
      • Gli extension methods per un IQueryable operano con Expression<Func<T>> generando un’expressione LINQ to SQL che è eseguita direttamente nel database layer
        • Ad esempio in un IQueryable il metodo Where viene esteso per poter accettare anche un’ Expression<Func>
        • La logica del filtro applicato è eseguita direttamente nel DB via EF o LINQ
      • Grazie all’extension methos AsQueryable() è possibile usare Func<TEntity, bool> come fosse Expression<Func<TEntity, bool>>.
        • La query non verrà eseguita finché non verrano invocati metodi come Count() o ToList().
          Func<App, bool> filter = x => x.Alias.Contains("gan");
          var query = dbSet.Where(filter).AsQueryable();

 

Novità C# (Records, Pattern matching enhancements, Global using directives, Null colascing assignements (?? e ??=), Null propagator, Advanced Tuples, Deconstruct; Discard, Local functions, Expession-bodied members, Ref locals and Ref return)

  • Null propagator (C# 6.0) 
    • //Evita che il valore null di un membro della catena ritorni un errore
      mycookie?.item?[key] 
  • Advanced Tuples (C# 7.0)
    • (string nome, string cognome, int age) persona = ("Nico", "Rossi", 43);
      
      var persona = (nome:"Nico", cognome:"Rossi", age:43)
  • Deconstruct (C# 7.0)
    • //La decostruzione è un processo di suddivisione di un'istanza di un oggetto (es; TupleValue) 
      //in più parti e di memorizzarle in nuove variabili.
      
      CustomData cm = new CustomData();
      cm.Name = <name>;
      cm.ThreadNum= <threadNum>;
      
      var (name, threaNum) = cm;
      
      Console.Write(name) //<name>
      
      public class CustomData
      {
        public CustomData(int name, int threadNum)
        { 
           Name= name;
           ThreadNum= threadNum;
         }
      
         public void Deconstruct(out int name, out int threadNum)
         {
            name = Name;
            threadNum = ThreadNum;
         }
      } 
  • Expression-bodied members (C# 7.0)
    • class Location
      {
        private string _name;
        
        //costruttori
        public Location(string name) => _name = name;
                    
        //proprietà
        public string Name
        {
            get => _name;
            set => _name = value;
        }
      
        //Metodi
        static string Duplica(string nome) => $"{nome}{nome}".Trim();
      
        //Indexers
        private String[] sports = { "Football", "Volley" };
        public string this[int index]
        {
            get => sports[index];
            set => sports[index] = value;
        }
      
        //Finalizer
        ~Location() => Console.WriteLine($"The {ToString()} finalizer is executing.");
      }
  • Discard (C# 7.0)
    • //standalone discard
      public static void Method(string arg)
      {
          _ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");
          // Do work with arg.
      }
      
      
      //Calls to methods with out parameters
      if (DateTime.TryParse(dateString, out _))
         Console.WriteLine($"'{dateString}': valid");
      else
         Console.WriteLine($"'{dateString}': invalid");
      
      
      //Tuple and object deconstruction
      var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);
  • Ref locals e Ref return (C# 7.0)
    • class Program
      {
          public ref int GetFirstOddNumber(int[] numbers)
          {
              for (int i = 0; i < numbers.Length; i++)
              {
                  if (numbers[i] % 2 == 1)
                  {
                      return ref numbers[i]; //returning as reference  
                  }
              }
              throw new Exception("odd number not found");
          }
      
          static void Main(string[] args)
          {
              Program p = new Program();
              int[] x = { 2, 4, 33, 92 };
      
              //memorizzo il ritorno della funzione in una variabile per referenza
              ref int oddNum = ref p.GetFirstOddNumber(x); //oddNum = 33
              
              //modifico la variabile 
              //avendo un legare per refenza con l'elemento dell'array x
              oddNum = 35;
              
              //allora anche tale elemento cambierà
              //x => { 2, 4, 35, 92 };
          }
      }
  • Span<T> (C# 7.2)
    • //é di tipo ref struct (memorizzato quindi nello stack)
      //non puo' essere un campo di una classe normale o di un struct non ref
      //non puo' essere usato come argomento o variabile locale in un metodo asincrono
      
      //lavoro con gli int 
      int[] _myArray = new int[9] {1,2,3,4,5,6,7,8,9};
      Span<int> Span() => _myArray.AsSpan().Slice(start: Size/2, length: Size/4);
      
      //lavoro con le stringhe
      ReadOnlySpan<char> span = "la mia stringa".AsSpan();
      span.Slice(start: 3); //mia stringa
  • Memory<T> (C# 7.2)
    • //E' un readonly strunc (non un ref struct)
      //simile a Span<T> ma puo' risiedere nell'HEAP
      //Un po' più lento di Span<T>
      
      private async Task SomethingAsync(Memory data)
      {
          SomethingSync(data.Span.Slice(1));
          await Task.Delay(1000); 
      }
      
      private void SomethingSync(Span data)
      {
          ...
      }
  • Null colascing assignements (?? e ??=) (C# 8.0) =>
    • Restituisce la parte sinistra se non null, altrimenti la parte destra
    • Usato anche con Throw come espressione
    • var a = b ?? c (a=b se b!=null, a=c se b=null)
    • b ==? c  (if b null allora = c) 
  • Local functions (C# 8.0)
    • //Simili alle lambda expressions
      //Permettono di scrivere funzioni disponibili solo all'interno di un metodo
      //Tutte le variabili e gli eventuali paramertri del metodo contenitore in una non-static funzione locale
      //E' possibile usare solo i modoficatori async o unsafe (di default sono private)
      
      //Sintassi di una 'local function'
      <modifiers> <return-type> <method-name> <parameter-list>
      
      // C# program to illustrate local function
      public class MyClass 
      {
         //Metodo contenitore
         public static void MyMethod()
         {
            // Local Function
            void AddValue<T>(T a, [out] T b)
            {
                 ...
            }
      
            // Calling Local function
            AddValue<int>(20, 40);
            AddValue<int>(40, 60);
         }
      }
      
      //Metodo per calcolare il fattoriale di un dato numero
      //Usa una 'local function' in modo ricorsivo
      public static int Fattoriale(int n)
      {
          return CalcolaFattoriale(n);
      
           int CalcolaFattoriale(int number) => (number < 2) ? 1 : number * CalcolaFattoriale(number - 1);
      }
  • Range operator (C# 8.0)
    • int[] oneThroughTen = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
      
      Write(oneThroughTen, ..);       // 0..^0:  1, 2, 3, 4, 5, 6, 7, 8, 9, 10
      Write(oneThroughTen, ..3);      // 0..3:   1, 2, 3
      Write(oneThroughTen, 2..);      // 2..^0:  3, 4, 5, 6, 7, 8, 9, 10
      Write(oneThroughTen, 3..5);     // 3..5:   4, 5
      Write(oneThroughTen, ^2..);     // ^2..^0: 9, 10
      Write(oneThroughTen, ..^3);     // 0..^3:  1, 2, 3, 4, 5, 6, 7
      Write(oneThroughTen, 3..^4);    // 3..^4:  4, 5, 6
      Write(oneThroughTen, ^4..^2);   // ^4..^2: 7, 8
  • Default interface methods (C# 8.0)
    • E’ possibile definire implementazioni concrete di un metodo in una interfaccia
    • public interface ILogger
      {
          void Info(string message);
          void Error(string message);
       
          // Default interface method
          void Warn(string message)
          {
              Debug.WriteLine(message);
          }
      }
      
      public class ConsoleLogger : ILogger
      {
          public void Error(string message)
          {
              Console.WriteLine(message);
          }
       
          public void Info(string message)
          {
              Console.WriteLine(message);
          }
      }
      
      ILogger logger = new ConsoleLogger();
      logger.Warn("<some message here">);
  • String interpolation using $ (C# 8.0)
    • {<interpolationExpression>[,<alignment>][:<formatString>]}
      
      $"***{Math.PI,20}***";      //***            3.14159265358979***
      $"***{Math.PI,-20:F3}***"   //***3.142                       ***
      
      //Interpolated expressions can include newlines (C#11.0)
      string message = $"The usage policy for {safetyScore} is {
          safetyScore switch
          {
              > 90 => "Unlimited usage",
              > 80 => "General usage, with daily safety check",
              > 70 => "Issues must be addressed within 1 week",
              > 50 => "Issues must be addressed within 1 day",
              _ => "Issues must be addressed before continued use",
          }
          }";
      
  • Asyncronous stream (C# 8.0)
    • //consume the stream data by data
      await foreach(var item in GetElements())
      {
         // do something
      }
      
      //produce a stream
      async IAsyncEnumerable<string> GetElements()
      {
          using var stream = new StreamReader("<file-path>");
      
          while(await stream.ReadLineAsync() is string line)
          {
              // do something
      
              yield return line;  
          }
      }
  • Pattern matching enhancements (C# 8.0)
    • Declaration pattern
      • int? xNullable = 7;
        int y = 23;
        object yBoxed = y;
        
        if (xNullable is int a && yBoxed is int b)
        {
           Console.WriteLine(a + b); // output: 30
        }
    • Constant pattern
      • if (input is not null)
        {
           // ...
        }
    • Switch expression
      • Constant pattern
        • public static decimal GetGroupTicketPrice(int visitorCount) => visitorCount switch
          {
              1 => 12.0m,
              2 => 20.0m,
              3 => 27.0m,
              4 => 32.0m,
              0 => 0.0m,
              _ => throw new ArgumentException($"Not supported number of visitors: {visitorCount}", nameof(visitorCount)),
          };
          
      • Relational pattern (compare an expression result with a constant)
        • cars!.GroupBy(c => c.HP switch
          {
            <= 100 => "0.100",
            <= 200 => "101.200",
            <= 300 => "201.300",
            <= 400 => "301.400",
            _ => "401.500"
          })
          .Select(group => new
          {
            HPCategory = group.Key,
            NumberOfMake = group.Select(m => m.Make).Distinct().Count()
          })
          .OrderBy(group => group.HPCategory)
          .ToList()
          .ForEach(item => Console.WriteLine($"{item.HPCategory} ({item.NumberOfMake})"));
          
      • Logical pattern
        • static string GetCalendarSeason(DateTime date) => date.Month switch
          {
              3 or 4 or 5 => "spring",
              6 or 7 or 8 => "summer",
              9 or 10 or 11 => "autumn",
              12 or 1 or 2 => "winter",
              _ => throw new ArgumentOutOfRangeException(nameof(date), $"Date with unexpected month: {date.Month}."),
          };
      • var pattern
        • public record Point(int X, int Y);
          
          static Point Transform(Point point) => point switch
          {
             var (x, y) when x < y => new Point(-x, y),
             var (x, y) when x > y => new Point(x, -y),
             var (x, y) => new Point(x, y),
          };
      • Positional patttern
        • public decimal CalculateDiscount(Order order) =>
               order switch
               {
                   not ( > 10, > 1000.00m) => 0.10m,
                   ( > 5, > 50.00m) => 0.05m,
                   { Cost: > 250.00m } => 0.02m,
                   null => throw new ArgumentNullException(nameof(order), "Can't calculate discount on null order"),
                   var someObject => 0m,
               };
          
          
      • Property pattern
        • static string CalculateCar(Car c) => c switch
          {
             string { Length: >= 5 } s => s.Substring(0, 5), 
             { IdCar: < 0, NameCar: "Mercedes" } => "La tua Mercedes é valida",
             Car { IdCar: var x } when x < 0 => "La tua vettura ha un problema", //case guards
              _ => throw new NotImplementedException(),
          };
          
      • Tuple pattern
        • static string CalculateCar(Car c) => c switch 
          {
             (1, "Fiat") => "Fiat id=1",
             (IdCar: 1, NameCar: "Fiat") => "Fiat id=1",
              _ => throw new NotImplementedException(),
          }
      • Type pattern
        • static int GetSourceLabel(IEnumerable source) => source switch
          {
              Array => 1,  (version C# 9.0)  
              oppure 
              Array _ => 1, (version C# 8.0) 
              ICollection collection => 2, (version C# 8.0) 
              _ => 3,
          };
      • List pattern (C# 11.0)
        • public static int CheckSwitch(int[] values) => values switch
          {
             [1, 2, .., 10] => 1,
             [1, 2] => 2,
             [1, _] => 3,
             [1, ..] => 4,
             [..] => 50
          };
      • Slice pattern (C# 11.0)
      • Span pattern (C# 11.0) 
  • Init oly setters (C# 9.0)
    • public struct WeatherObservation
      {
         public DateTime RecordedAt { get; init; }
         public decimal TemperatureInCelsius { get; init; }
        
         public override string ToString() =>
           $"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
           $"Temp = {TemperatureInCelsius};
      }
      
      //le 3 proprietà possono essere assegnate quando inizializzo un nuovo oggetto della classe
      var now = new WeatherObservation 
      { 
         RecordedAt = DateTime.Now, 
         TemperatureInCelsius = 20
      };
      //ma non direttamente tramite la proprietà
      //ERROR
      now.RecordedAt = DateTime.Now;
  • Records (C# 9.0) =>
    • E’ un nuovo modo per definire una classe.
    • E’ una classe con delle proprietà.
    • Adatto per implementare DTO.
    • E’ immutabile.
    • E’ possibile ereditare da un record.
    • Ispezionando il codice IL anche del più semplice record si vedranno molte linee di codice
      • E’ implementato nativamente il deconstruct
      • E’ implementato nativamente il compare basato sul valore (e non la referenza come nelle classi normali)
    • -nuova sintassi
      record Hero(string HeroName, bool CanFly)
      {
         //aggiungo cun deconstrcut custom (oltre quello built-in)
         public void Deconstruct(
            out string HeroName,
            out bool CanFly,
            out bool isArchived
         )
      };
      
      -vecchia sintassi
      class Hero
      {
         public Hero(string heroName, bool canFly)
         {
            HeroName = heroName;
            CanFly = canFly;
         }
      
         public string HeroName { get; set;}
         public bool CanFly { get; set;}
      }
  • Record struct (C#10.0)
    • Non é immutabile
    • E’ immutabile usando readonly
    • Cane cane = new("Fuffy", 4);
      cane.name = "Boby";
      //Mutabile
      public record struct Cane(string name, int age);
      
      Cane cane = new("Fuffy", 4);
      cane.name = "Boby"; //ERRORE
      
      //Immutabile
      public readonly record struct Cane(string name, int age);
  • File-scoped namespace declaration (C# 10.0)
    • //myfile.cs
      
      namespace <my-namespace>;
      //....
      //....
  • Inferred Delegate type (C# 10.0)
    • //Prima di C# 10.0
      Func<string,int> parse = (string conta) => int.Parse(conta);
      
      //Dopo C# 10.0
      var parse = (string conta) => int.Parse(conta);
  • Attributes in lambas (C# 10.0)
    • Gli attributi possono essere applicati alle espressioni lambda.
  • Using directives (C# 10.0)
    • //global modifier
      global using System;
      
      //static modifier
      using static System.Math;
      
      //using alias (for a class)
      using MyAlias = <my-namespace>.MyClass;
      namespace <my-namespace>
      {
          class MyClass
          {
          }
      }
      var instance2 = new MyAlias();
      
    • Se attivato, permette di creare un file che contiene degli statement using globali utilizzabili in ogni cs file del progetto
    • Il file creato è obj/Debug/net6.0/<ProjectName>.GlobalUsings.g.cs
    • global using global::System
    • Attivare/Disattivare 
    • //Aprire il file .csproj e impostare il tag <ImplicitUsings> = enable | disabled
      <Project Sdk="Microsoft.NET.Sdk">
        <PropertyGroup>
          <ImplicitUsings>enable</ImplicitUsings>
        </PropertyGroup>
      </Project>
  • nameof(parameter) (C# 11.0)
    • Questa funzionalità significa che è possibile utilizzare l’operatore nameof per specificare il nome di un parametro del metodo in un attributo sulla dichiarazione del metodo o del parametro.
    • Questa funzione è spesso utile per aggiungere attributi per l’analisi nullable.
  • Cache delegate  for static method group (C# 11.0)
  • Required properties (C# 11.0)
    • È possibile aggiungere il modificatore required alle proprietà e ai campi per imporre ai costruttori di inizializzare tali valori.
    • class Order
      {
         public required string Name { get; init;}
      }
  • Raw string literals (C# 11.0)
    • var location = $$"""
                        You are at {{{Longitude}}, {{Latitude}}}
                        Ipse dixit Ipse dixit Ipse dixit Ipse dixit 
                       """;
  • Static abstract members in interface (C# 11.0)
    • // Interface has static abstract members
      public interface ISport
      {
          static abstract bool IsTeamSport();
      }
      
      // If the static member is not implemented by class, 
      // it results in compilation error like other non-static interface methods.
      public class Bowling : ISport
      {
           public static bool IsTeamSport()
           {
              return false;
           }
      }
      
      public class Swimming : ISport
      {
           public static bool IsTeamSport()
           {
             return false;
           }
      }
      
      
      //-------------------------------------------------------------------
      // Generic method to call the static abstract method
      //-------------------------------------------------------------------
      void Display(T sport) where T: ISport
      {
          Console.WriteLine("Is Team Sport:" + T.IsTeamSport());
      }
      
      //-------------------------------------------------------------------
      // Driver program
      //-------------------------------------------------------------------
      var bowling = new Bowling();
      Display(bowling);           // Generic method call
      
      var swimming = new Swimming(); 
      Display(swimming); // Generic method call 
      
  • UTF8 String literals (C# 11.0)
    • byte[] array = "hello"; // new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f }
      Span<byte> span = "dog"; // new byte[] { 0x64, 0x6f, 0x67 }
      ReadOnlySpan<byte> span = "cat"; // new byte[] { 0x63, 0x61, 0x74 }
  • Generic attribute (C# 11.0)
    • // Generic Attributes with C# 11
      //--------------------------------------------------------
      // CustomDoNothing attribute - A generic class now
      // Constraints are specified on type parameters.
      //--------------------------------------------------------
      
      [AttributeUsage(AttributeTargets.Class)]
      public class CustomDoNothingAttribute<T>: Attribute where T : class
      {
          public CustomDoNothingAttribute()
          {
          }
      
          public T ParamType { get; }
      }
      
      //--------------------------------------------------------
      // CustomDoNothing attribute is placed on a class
      // The type is set to be string.
      //--------------------------------------------------------
      
      [CustomDoNothing<string>()]
      public class Student
      {
          public int Id { get; set; }
      
          public string Name { get; set; }
      }
  • Parameter null-checking (!!) (C# 11.0)
    • //Prima
      public static void M(string s)
      {
        if (s is null)
        {
           throw new ArgumentNullException(nameof(s));
        }
        // Body of the method
      }
      
      //Dopo
      public static void M(string s!!)
      {
         // Body of the method
      }

Benchmark.Net

  • Installare il package 'BenchmarkDotNet'
    
    using BenchmarkDotNet.Attributes;
    using BenchmarkDotNet.Jobs;
    //Come lanciare il benchmark (RELEASE mode)
    //Per testare devo creare una nuova 'console application'
    BenchmarkRunner.Run<Demo>();
    
    //Come decorare la classe che si vuole benchmarkare
    //Posso lanciare il benchmark avendo come target un framework preciso
    [SimpleJob(RuntimeMoniker.Net70, baseline:true)]
    [SimpleJob(RuntimeMoniker.Net50)]
    [MemoryDiagnoser]
    public class Demo
    {
       [GlobalSetup]
       public void Setup() 
       {
           //this code runs once as code starts
       }
       
       //Posso impostare uno o più parametri 
       //Avro' un esecuzione del benchmark per ognuno dei parametri indicati.
       [Params(10, 100)]
       public int Size { get; set; }
    
    
       [Benchmark(Baseline = true)]
       public string GetFullStringV1()
       {
           //Questa é la funzione di base che si vuole confrontare con altre versioni scritte dopo
       }
    
       [Benchmark]
       public string GetFullStringV2()
       {
           //Questa é una nuova versione della funzione base 
           //voglio vedere quali risultati mi dà rispetto all'altra
       }
    
       [GlobalCleanup]
       public void GlobalCleanup() 
       {
          //this code runs once as code stops
       }
    }
    
    
    //.csproj
    //Modificare il file .csproj per aggiungere tutte le versioni .NET che si vogliono usare nei nostri test
    <PropertyGroup>
     <TargetFrameworks>net5.0;net7.0</TargetFrameworks>
    </PropertyGroup>
  • Nullable reference types (C# 8.0)
    • Controllo a compile-time delle nullabilità variabili di tipo referenze.
    • A runtime non c’é differenza tra una variabile nullable e la stessa non-nullable.
      • //Variabili non-nullable => Devono avere un valore
        string notNull = "Hello";
        
        //Variabili nullable => Devono essere assegnate a null o usare il 'null forgiveness'
        string? nullable = default;
        notNull = nullable!; // null forgiveness 
        oppure faccio esplicitamente il check
        if(nullable is not null)
           notNull = nullable;

 

Risorse

  • .NET core source broswer 
  • Perfview 
    • E’ uno strumento di analisi delle prestazioni della CPU e della memoria
    • Per esempio posso vedere quanto tempo delle CPU utilizza il Garbage Collector 
  • Mockaroo
    • Fornisce dati di test
    • E’ possibile creare delle WEB API che restituiscono il set di dati desiderato 
    • Le WEB API sono ospitate nel tuo account del loro sito