C # 6.0 caratteristiche che ogni sviluppatore ASP.NET dovrebbe conoscere


Visual Studio 2015 introduce una nuova versione di ASP.NET, ed include anche la versione 6 del linguaggio C#


A cura di Giovanni Arcifa,


C # 6.0  caratteristiche che ogni sviluppatore ASP.NET dovrebbe conoscere

Le Precedente versioni di C# hanno annunciato modifiche sostanziali, come l'introduzione di async e await nella versione 5, dynamic nella versione 4, LINQ in versione 3 e così via. In C# 6 non ci sono grosse novità o modifiche delle funzionalità principali, ma ci sono una serie di miglioramenti sintattiche che rendono il linguaggio un po’ più semplice da usare e veloce da scrivere. In questo articolo passeremo in rassegna le più importanti. A titolo di esempio creiamo un paio di classi che potrebbero formare un modello molto semplice in qualsiasi applicazione:

 

public class Person
{
    public Person()
    {
        FirstName = string.Empty;
        LastName = string.Empty;
        DateCreated = DateTime.UtcNow;
        Qualifications = new HashSet<Qualification>();
    }
    public int PersonId { get; set; }
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string FullName { get { return string.Format("{0] {1}", FirstName, LastName); } }
    public DateTime DateCreated { get; set; } 
    public DateTime BirthDate { get; set; }
    public ICollection<Qualification> Qualifications { get; set; } 
}
public class Qualification
{
    public Qualification()
    {
        Awardees = new HashSet<Person>();
        Name = string.Empty;
    }
    public int QualificationId { get; set; }
    public string Name { get; set; } 
    public DateTime WhenAwarded { get; set; }
    public virtual ICollection<Person> Awardees { get; set; } 
}

Il modello contiene una classe Person ed una classe Qualification, con una relazione molti-a-molti tra loro. Come si può notare dal codice sopra esposto abbiamo fatto largo uso delle auto properties in quanto non c’è logica da eseguire per il set o il get dei valori delle proprietà. La proprietà FullName della classe person è ottenuta dalla concatenazione delle proprietà FirstName e LastName. I valori di default di alcune proprietà sono definite nel costruttore. Le prime 3 nuove features di C# 6 sono mirate a migliorare la sintassi quando si definiscono classi semplici come quelle appena viste. In seguito vedremo come cambia la scrittura della classe Person utilizzando le nuove funzionalità.

1. Auto-property initializers

Se si vuole settare un valore di default ad una proprietà di una classe, possiamo utilizzare il costruttore in C# 5 o precedenti. C# 6 introduce invece le Auto-property initializers che consentono di assegnare un valore predefinito ad una proprietà durante la dichiarazione della proprietà stessa. Quindi utilizzando le auto-property initializers la classe Person diventa:

public class Person
{
    public int PersonId { get; set; }
    public string FirstName { get; set; } = string.Empty;
    public string LastName { get; set; } = string.Empty;
    public string FullName { get { return string.Format("{0] {1}", FirstName, LastName); } }
    public DateTime DateCreated { get; set; } = DateTime.UtcNow;
    public DateTime BirthDate { get; set; }
    public ICollection<Qualification> Qualifications { get; set; } = new HashSet<Qualification>();
}

 

2. Expression bodied members

La prorietà FullName della classe Person è in sola lettura, ed il valore della proprietà e calcolato. In C# 6 posso ridurre un po’ la scrittura di codice utilizzando un meccanismo simile alle auto-property initializer:

public string FullName => string.Format("{0} {1}", FirstName, LastName);

Il simbolo => in questo caso non denota un'espressione lambda e lo possiamo utilizzare anche per definire il corpo di un metodo. Ad esempio, è possibile scegliere di esporre l'età di una persona come un metodo chiamato GetAge() in questo modo:

public TimeSpan GetAge()
{
    return DateTime.Now - BirthDate;
}

Che utilizzando l’expression body function diventa:

public TimeSpan GetAge() => DateTime.Now - BirthDate;

 

3. Getter-only auto-properties

Quando usiamo le auto implemented properties in C# 5 o precedenti, e necessario fornire un get e un set. Se si desidera che il valore della proprietà sia immutabile, è possibile utilizzare il private sul setter, ma ciò è sempre stato considerato un po’ a rischio hack in quanto non rende la proprietà modificabile dall’esterno ma sarà comunque modificabile all’interno dalla classe. Con C# 6, ora è possibile omettere il set nelle auto implemented properties ottenendo effettivamente una proprietà di sola lettura:

public DateTime BirthDate { get; }

 

4. String interpolation

Fino ad ora, abbiamo utilizzato string.Format per ottenere il valore della proprietà FullName della classe Person. Personalmente ho sempre trovato un po’ goffo il suo utilizzo anche se preferivo utilizzarlo per non avere un sacco di operatori di concatenazione sparsi per tutto il codice. C# 6 mette a disposizione una nuova funzione denominata string interpolation che di tutte le novità di C# 6 è probabilmente la mia preferita. Con la string interpolation è possibile formattare stringhe utilizzando parametri direttamente all'interno della stringa e con l'aiuto dell'IntelliSense senza più i placeholder numerici, consentendo di scrivere codice molto più pulito ed elegante rispetto a String.Format

public string FullName => $"{FirstName} {LastName}";

In questo modo evitiamo di scrivere gli indici, e riduciamo (se non eliminiamo) la possibilità di FormatException generata quando inavvertitamente si forniscono troppo pochi valori per la lista degli argomenti

5. Null-conditional operators

var people = new List<Person>();
var name = string.Empty;
if(people.FirstOrDefault() != null)
{
    name = people.First().FullName;
}

Quante volte abbiamo scritto codice che controlla il valore null per evitare un possibile NullReferenceException quando si tenta di fare riferimento ad una proprietà di un oggetto? Se la risposta è "troppo", probabilmente inizierete ad amare il null conditional operator. L'operatore null-conditional, permette di controllare il fatto che un oggetto sia null, ed evitare di incorrere nelle comuni NullReferenceException, riducendo la quantità di codice ripetitivo da scrivere. L'operatore ? permette di accedere a membri di un oggetto solamente quando l'oggetto stesso non è null, restituendo in caso contrario il valore null. Supponiamo ad esempio di avere una classe Persona così fatta:

var people = new List<Person>();
var p= people.FirstOrDefault();
var name = p?.FullName;

In tal modo, se p è null, la variabile name assumerebbe valore null, altrimenti il valore della proprietà Fullname. L'operatore è utilizzabile in maniera molto efficace in congiunzione con l'operatore ?? di null coalescing. Se volessimo infatti assegnare un valore diverso da null alla variabile name, potremo scrivere:

string name=p?.FullName ?? "senza nome"; //se p è null restituisce "senza nome"

6. Using static

Questa funzionalità permette di accedere ai metodi statici di una classe importando il nome della classe attraverso using e utilizzando la parola chiave static. In questo modo posso utilizzare i metodi statici di una classe esterna come se fossero dei metodi della mia classe senza specificare il nome della classe di provenienza prima dell’invocazione del metodo. Ad esempio, questo è quello che dovrei scrivere per poter utilizzare i metodi statici della classe System.IO.File:

using static System.IO.File;

A questo punto possiamo usare i metodi statici senza invocare il nome della classe:

var file = @"C:\test.txt";
if (!Exists(file))
{
    Create(file);
}

Personalmente questa feature non mi piace molto perché rende la provenienza del metodo poco chiara e nel caso di classi che hanno metodi con lo stesso nome dovrei comunque specificare i nomi delle classi per evitare le collisioni.

7. Index initializers

Questa funzione fornisce un nuovo modo per inizializzare collezioni basati su indici, come i dizionari. In precedenza, per inizializzare un dictionary avrei scritto:

Dictionary<int, string> dict = new Dictionary<int, string>
{
    {1, "string1" },
    {2, "string2" },
    {3, "string3" }
};

Adesso possiamo scrivere:

Dictionary<int, string> dict = new Dictionary<int, string>
{
    [1] = "string1",
    [2] = "string2",
    [3] = "string3" 

};

Ovviamente il tipo contenuto nella chiave deve essere congruente con il tipo definito nel Dictionary.