Anonymous type di C# 3.0

di Cristian Civera, in .NET 3.0,

Giocando con LINQ avrete senz'altro usato gli anonymous type: quei tipi creati al volo per contenere varie informazioni. Poniamo questo semplice esempio:

var v = new {Language = "IT", Age=30};

Prima di tutto, v è visibile solo all'interno dello stack in cui l'abbiamo dichiarato. Quindi all'interno di una funzione o metodo, ma mai a livello di classe. Possiamo anche ritornarlo come valore da una funzione, ma potremmo limitarci a ritornare un tipo object generico e non potremmo fare riferimento al nome del tipo, facendo cast o quant'altro (se non via reflection, bleah bleah). Quindi ques'ultima opzione è fortemente sconsigliata.

Quando compiliamo, viene automaticamente generata una classe internal sealed dal nome f__AnonymousType[numero] sul namespace root. La classe eredita da Object definisce n proprietà e n campi per memorizzare i valori e dispone di un costruttore che accetta come parametri i valori da impostare sulle proprietà. Non c'è modo di cambiarne visibilità ai tipi o ai membri, ne di rendere in sola lettura le proprietà. Questa classe ha la particolarità di essere generica, nel senso che i tipi string e int delle proprietà Language e Age del nostro esempio, sono generici. Questo per evitare di creare copie della medesima classe. Quindi se utiliziamo nel medesimo assembly un anonymous type simile:

var v = new {Language = 1, Age=3d};

verrà utilizzata la medesima classe autogenerata, ma con generics argument diversi: int e double.

Tale classe inoltre sovrascrive ToString per mostrare facilmente il contenuto, Equals e GetHashCode così da rendere comparabile il tipo quando viene utilizzato, per esempio da LINQ To Objects:

[DebuggerHidden] 
public override string ToString() 
{ 
    StringBuilder builder = new StringBuilder(); 
    builder.Append("{ Language = "); 
    builder.Append(this.<Language>i__Field); 
    builder.Append(", Age = "); 
    builder.Append(this.<Age>i__Field); 
    builder.Append(" }"); 
    return builder.ToString(); 
} 
 
[DebuggerHidden] 
public override int GetHashCode() 
{ 
    int num = 0x5b485bf4; // Numero casuale 
    num = (-1521134295 * num) + EqualityComparer<<Language>j__TPar>.Default.GetHashCode(this.<Language>i__Field); 
    return ((-1521134295 * num) + EqualityComparer<<Age>j__TPar>.Default.GetHashCode(this.<Age>i__Field)); 
} 
 
[DebuggerHidden] 
public override bool Equals(object value) 
{ 
    var type = value as <>f__AnonymousType0<<Language>j__TPar, <Age>j__TPar>; 
    return (((type != null) && EqualityComparer<<Language>j__TPar>.Default.Equals(this.<Language>i__Field, type.<Language>i__Field)) && EqualityComparer<<Age>j__TPar>.Default.Equals(this.<Age>i__Field, type.<Age>i__Field)); 
}

Sono interessanti l'uso di DebuggerHidden per evitare di far vedere dal debugger tali metodi e l'uso di EqualityComparer.Default per ottenere l'hashcode o comparare i tipi primitivi utilizzati. EqualityComparer.Default restituisce un IEqualityComparer<T> (fondamentale in LINQ) a seconda che il tipo implementi IEquatable<T>, sia nullabile o come ultima spiaggia, sovrascriva Equals e GetHashCode. Per comparare le stringhe (utile sempre in LINQ) abbiamo a disposizione la classe StringComparer che permette diversi modi di comparazione delle stringhe: CurrentCulture, CurrentCultureIgnoreCase, InvariantCulture, InvariantCultureIgnoreCase, Ordinal, OrdinalIgnoreCase.

Commenti

Visualizza/aggiungi commenti

| Condividi su: Twitter, Facebook, LinkedIn

Per inserire un commento, devi avere un account.

Fai il login e torna a questa pagina, oppure registrati alla nostra community.

Nella stessa categoria
I più letti del mese