Programming Languages Hacks

Importanti regole per linguaggi di programmazione rilevanti come Java, C, C++, C#…

  • Subscribe

  • Lettori

    I miei lettori abituali

  • Twitter

.NET 3.5: le Novità del Linguaggio C#

Posted by Ricibald on March 6th, 2008

Nella specifica c# 3.0 sono riportate le principali novità nel linguaggio C#. Tranquilli, la maggior parte sono solo utile zucchero sintattico.

Automatic Properties

Invece di scrivere:

public class Persona {
    private int _matricola;
    public int Matricola {
        get {
            return _matricola;
        }
    }
    private string _nome;
    public int Nome {
        get {
            return _nome;
        }
        set {
            _nome = value;
        }
    }

    public Persona(int matricola) {
        _matricola = matricola;
    }
}

Ora scriveremo:

public class Persona {
    public string Matricola { get; private set; }
    public string Nome { get; set; }
    public Persona(int matricola) {
        this.Matricola = matricola;
    }
}

Il compilatore crea automaticamente i corrispondenti campi privati e le relative implementazioni get/set.

Nel caso però in cui il metodo get/set debba essere personalizzato, allora è necessario ricorrere al vecchio stile di definizione delle proprietà.

Object Initializer

Invece di scrivere :

Persona persona = new Persona(15);
persona.Nome = "Riccardo";
persona.Anni = 25;

Scriveremo:

Persona persona = new Persona(15) { Nome = "Riccardo", Anni = 25 };

Rappresenta quindi solamente un modo per scrivere in modo compatto il codice precedente. Non deve quindi essere visto come un modo per
rimpiazzare la logica dei costruttori
.

Collection Initializers

Un Object Initializer particolare appartiene ai tipi ICollection che supportano i Collection Initializer:

Persona persona = new Persona(15) { Nome = "Riccardo", Anni = 25 };
ICollection
<Persona> personas = new List<Persona>
 { persona };

Sinceramente mi sembra solo un modo per uniformare il popolamento delle collezioni al popolamento degli array.
Infatti, per avere lo stesso comportamento sarebbe stato sufficiente aggiungere un costruttore con la signature:

public List(params T[] ts)

e non sarebbe stato necessario inserire ulteriore sovrabbondante “zucchero sintattico”.

Partial Methods

Le classi scritte dal designer sono classi parziali. Ma se si vogliono definire dei punti estensibilità per la classe autogenerata (OnAction()) avremmo bisogno non di una classe parziale, ma di una classe padre da cui eredita la nostra classe. Questo non è conveniente poiché ci leghiamo definitivamente a una classe padre. Per ovviare a questo problema sono stati definiti i metodi parziali, un modo per definire opzionalmente dei metodi nella nostra classe.

Quindi se nel designer abbiamo:

public partial class Studente {
    private int _matricola;
    public int Matricola {
        get { return _matricola; }
        set {
            this.OnMatricolaChanging(value);
            _matricola = value;
    }
    partial void OnMatricolaChanging(int m);
}

Mentre nella nostra classe avremo:

public partial class Studente {
    partial void OnMatricolaChanging(int m) {
        if (m.ToString().Length == 5) {
            throw new Exception("Matricola non valida!");
        }
    }
}

Lambda Expressions

Ricordate il post dove parlavo della potenza dei delegati anonimi?
Bene, questo è altro zucchero sintattico per scrivere in maniera compatta delegati anonimi. Immaginiamo quindi di aver definito il seguente delegato anonimo e il metodo che ne fa uso:

public class SearchFunctions {
    public delegate bool SearchFunction<T>(T arg);
    public static T Search<T>(IEnumerable<T> c, SearchFunction<T> s);
}

Verrà invocato nel seguente modo:

ICollection
<Persona> personas = new List
<Persona> { new Persona(15), new Persona(23) };
Persona p = SearchFunctions.Search(personas, delegate(Persona personaDaVerificare) {
    return personaDaVerificare.Matricola == 23;
});

Tramite le Lambda Expressions, che si basano sul Lambda Calcolo, è possibile esprimere in modo compatto l’espressione:

Persona p = SearchFunctions.Search(personas, personaDaVerificare => personaDaVerificare.Matricola == 23);

Si noti come non è stato inserita alcuna informazione sul tipo della variabile “personaDaVerificare”: tale tipo verrà automaticamente dedotto.

Extension Methods

Ora arriviamo ai due punti per me più discussi, che (secondo me) non hanno senso di esistere a meno di eccezioni molto particolari.
Gli Extension Method consentono di estendere le funzionalità del framework. Si consideri ad esempio:

string email = "prova"
if ( EmailValidator.IsValid(email) ) { /* Implementazione */ }

Tramite gli Extension Method possiamo aggiungere funzionalità direttamente dentro la classe string:

public static class MyExtensions
{
    public static bool IsValidEmailAddress(this string s) { /* ... */ }
}

Si noti il “this” davanti al parametro: indica che vado ad aggiungere un metodo alla classe string.
Si possono invocare tramite:

using MyExtensions;

/* ... */
string email = "prova"
if ( email.IsValidEmailAddress() ) { /* Implementazione */ }

Questo utilizzo degli Extension Method lo considero una specie di enorme bug: capire l’organizzazione di un codice scritto in questa maniera diventa un incubo. Non posso mica conoscere tutti i metodi di tutte le classi, quindi se leggo codice scritto da altri dovrei quantomeno assumere che il codice proveniente da classi di .NET siano funzionanti o no? In questo modo sono costretto a ispezionare tutti i riferimenti alla ricerca di un eventuale Extension Method prima di dire “ok, iniziamo”. E poi era così brutto richiamare l’EmailValidator? A me non sembrava…

Però su una cosa sono utili: non prevedono di conoscere l’EmailValidator: le funzionalità sono già fornite come built-in del tipo. Risulta molto comodo quindi, nel caso in cui queste funzionalità sono state scritte anche anni fa (e chi se le ricorda più…). Se si creano extension method con lo stesso namespace del tipo che estendiamo avremo funzionalità aggiuntive “sempre” disponibili, e non utilizzabili solo se la “memoria dello sviluppatore” lo permette…

L’unico caso (secondo me) in cui risulta utile è quando il tipo restituito dall’Extension Method è lo stesso del tipo modificato:

public static T MyExtensionMethod(this T t /*, ... */)

In questo caso risulta comodo, poiche’ assume il ruolo di decoratore o filtro. Se infatti creiamo gli Extension Method:

public static class MyExtensions
{
    public static int MescolaCifre(this int i) { }
    public static int TogliSegno(this int i) { }
    public static int RendiDivisibilePer(this int i, int d) { }
}

Si può quindi utilizzare nel seguente modo:

using MyExtensions;

/* ... */
int i = 58935728957.MescolaCifre().TogliSegno().RendiDivisibilePer(5);

Anonymous Types

L’ultimo da discutere sono i tipi anonimi. In pratica invece di scrivere:

string nome = "Riccardo";
int matricola = 15;
int anni = 25;
nome = nome.ToUpper();
matricola++;

posso scrivere:

var nome = "Riccardo";
var matricola = 15;
var anni = 25;
nome = nome.ToUpper();
matricola++;

e il tipo viene “magicamente” dedotto dal compilatore.

Spero direte: “E allora? Non è un evoluzione, ma un tornare indietro“. Vero! Ma esistono casi particolari in cui ha senso.

L’unico utilizzo a mio parere giustificato dei tipi impliciti stà:

  • nell’uso delle funzioni lambda dove di fatto avviene una tipizzazione implicita
  • dove non ha senso parlare di tipi: ad esempio in tuple restituite da una query sql. In questo caso, viceversa, risulta una forzatura definire un tipo a tutti i costi, solo per avvolgere valori

Leave a Reply

You must be logged in to post a comment.