LINQ è la magia che .NET ha dimostrato di avere negli ultimi anni. Basato su una monade (una struttura matematica) decisamente semplice, LINQ semplifica la vita del developer e dà un tocco di classe al codice, come mai prima. I numerosi interventi nel linguaggio, con l'avvento di 3.0, hanno permesso di costruire LINQ, in 3.5, in maniera robusta, e darci oggi forse il motore di interrogazione universale che si aspettava da tempo.
Abbiamo ad oggi diversi provider di LINQ, in circolazione:

  • LINQ to Objects => forse il più rilevante, poichè interroga qualsiasi sorgente ad oggetti.
  • LINQ to XML => permette l'accesso e la interrogazione su XML, direttamente (anche su mobile).
  • LINQ to Quasi-Tutto-E-Comunque-Tutto-Ciò-Per-Cui-Si-Implementa-Il-Provider.

Fondamenti (ovvero i tasselli fondamentali per poter usare LINQ)
Nella tipica query LINQ il developer "figo" scrive:

var qualcosa= ... //query complessa

"var" sta ad indicare non il tipo Variant del vecchio VB, o comunque un tipo dati "generico" che fa da maxi-container per qualsiasi tipo dati; bensì è solo un modo per dire al compilatore "pensaci tu ad inferire il tipo, che io (dev) non ho voglia di scriverlo".
Infatti una istruzione così:
var a;
a=10;

porta a inferire che il tipo di "a" sia un intero; così invece:
var b;
b=new StreamWriter(...);

porta a inferire che "b" sia un oggetto StreamWriter. Il tutto è ovviamente type-safe poichè in fase di compilazione a e b, sono rispettivamente oggetti fortemente tipizzati.

Spesso poi ci è capitato di volere un costruttore con tutti i parametri convergenti alle public properties del nostro oggetto (per poterle assegnare in costruzione); questa cosa, per l'appunto, necessita l'implementazione di uno o più costruttori atti allo scopo. Invece nuova è la possibilità di istanziare un oggetto e assegnarne già le proprietà, in questo modo:

var pers = new Persona()
{
    Cognome = "Freato",
    Nome = "Roberto",
    DataNascita = DateTime.Parse("12/14/1984"),
    Telefono = "+39123456789",
    Indirizzo = new IndirizzoStruct()
    {
        CAP="20100",
        Citta="Milano",
        Civico="1",
        Nome="Duomo",
        Tipo=TipoVia.Piazza
    }
};

Da qui possiamo arrivare in un passo ai tipi anonimi, cioè a oggetti la cui classe non è dichiarata da nessuna parte. Il compilatore poi creerà la classe necessaria ad ottenere il giusto comportamento (consistente con tutto il resto dell'ambiente) ma a noi non verrà chiesto nulla più di questa riga:
var variabileDiUnMioTipoInventatoOra =
    new { CoordX = 10, CoordY = 20, CoordZ = 30 };

Con la riga sopra ho dichiarato una varabile di un tipo che ho "inventato" al momento, rappresentante una coordinata 3D. Il compilatore creerà una classe con tre proprietà di tipo int e nomi, rispettivamente CoordX,Y e Z.

LAMBDA Expressions
Le espressioni LAMBDA rappresentano il sogno di molti, che possono vedere finalmente l'eleganza matematica, dentro C#. Una espressione lambda è una funzione che non viene scritta nel corpo di una classe, bensì all'interno di una funzione: esattamente come dichiarare una variabile.

delegate int TipoFunc(int a);
public void Operazioni()
{
    //operazioni
    TipoFunc potenza = (a) => { return a * a; };
    int res = potenza(10);
}

Una siffatta dichiarazione intanto, porta il corpo della funzione (a*a) ad essere messo nello heap; secondo permette alle espressioni di essere compilate in differita. L'esempio di prima lo possiamo portare anche in una forma più matematica, pensando alla funzione così:
"La funzione potenza è definita da R a R^2 per cui P: R=> R^2" da cui si ha il seguente costrutto, che è infatti di codominio R^2:
TipoFunc potenza = a => a * a;

Dei metodi di estensione abbiamogià parlato, quindi ora passiamo a LINQ, iniziando da un esempio:

DirectoryInfo info=new DirectoryInfo(@"x:\");
var hugeDirs=info.GetDirectories()
    .Where(p=>p.GetFiles().Count()>100)
    .Select(p=> new {Dir=p.Name,Files=p.GetFiles()});

In questo caso abbiamo:

  • Variabile Implicita => hugeDirs
  • Tipo anonimo => hugeDirs verrà creato come oggetto avente due properties (Dir=>string e Files=>FileInfo[])
  • Metodo di estensione => Where e Select che utilizzano con oggetto il risultato del metodo precedente.
  • LAMBDA Expression => nel corpo del where e del select.

LINQ supporta la valutazione lazy, per cui i dati possono essere prelevati SOLO nel momento in cui vengono effettivamente richiesti da una valutazione.
Questo, vedremo poi, ci permette di costruire anche collezioni immaginarie di tutti i numeri naturali (o reali, o primi, o come-vogliamo), per esempio, senza portare in blocco la cpu.
Da qui si apre un mondo che, se il tempo mi permetterà, approfondirò nella sezione apposta dedicata a LINQ.