Tipi anonimi

I tipi anonimi offrono un modo pratico per incapsulare un set di proprietà di sola lettura in un singolo oggetto, senza dover definire prima un tipo in modo esplicito. Il nome del tipo viene generato dal compilatore e non è disponibile a livello di codice sorgente. Il tipo di ogni proprietà è dedotto dal compilatore.

Per creare tipi anonimi, si usa l'operatore new insieme a un inizializzatore di oggetto. Per altre informazioni sugli inizializzatori di oggetto, vedere Inizializzatori di oggetto e di raccolte.

L'esempio seguente mostra un tipo anonimo inizializzato con due proprietà denominate Amount e Message.

var v = new { Amount = 108, Message = "Hello" };

// Rest the mouse pointer over v.Amount and v.Message in the following
// statement to verify that their inferred types are int and string.
Console.WriteLine(v.Amount + v.Message);

I tipi anonimi vengono in genere usati nella clausola select di un'espressione di query per restituire un subset delle proprietà da ogni oggetto della sequenza di origine. Per altre informazioni sulle query, vedere LINQ in C#.

I tipi anonimi contengono una o più proprietà pubbliche di sola lettura. Non sono validi altri tipi di membri della classe, ad esempio metodi o eventi. L'espressione usata per inizializzare una proprietà non può essere null, una funzione anonima o un tipo di puntatore.

Lo scenario più comune consiste nell'inizializzare un tipo anonimo con proprietà di un altro tipo. Nell'esempio seguente si presuppone l'esistenza di una classe denominata Product. La classe Product include le proprietà Color e Price, insieme ad altre proprietà non pertinenti. La variabile products è una raccolta di oggetti Product. La dichiarazione di tipo anonimo inizia con la parola chiave new. La dichiarazione inizializza un nuovo tipo che usa solo due proprietà della classe Product. L'uso di tipi anonimi comporta la restituzione di una quantità minore di dati nella query.

Se nel tipo anonimo non si specificano i nomi dei membri, il compilatore assegna ai membri del tipo anonimo lo stesso nome della proprietà usata per inizializzarli. Specificare un nome per una proprietà inizializzata con un'espressione, come illustrato nell'esempio precedente. Nell'esempio seguente i nomi delle proprietà del tipo anonimo sono Color e Price.

var productQuery =
    from prod in products
    select new { prod.Color, prod.Price };

foreach (var v in productQuery)
{
    Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}

Suggerimento

È possibile usare la regola di stile IDE0037 di .NET per stabilire se sono da preferire i nomi dei membri dedotti o espliciti.

È anche possibile definire un campo per oggetto di un altro tipo: classe, struct o anche un altro tipo anonimo. Si esegue usando la variabile che contiene questo oggetto, proprio come nell'esempio seguente, in cui vengono creati due tipi anonimi usando tipi definiti dall'utente già creati. In entrambi i casi il campo product nel tipo anonimo shipment e shipmentWithBonus sarà di tipo Product contenente i valori predefiniti di ogni campo. E il campo bonus sarà di tipo anonimo creato dal compilatore.

var product = new Product();
var bonus = new { note = "You won!" };
var shipment = new { address = "Nowhere St.", product };
var shipmentWithBonus = new { address = "Somewhere St.", product, bonus };

In genere, quando si usa un tipo anonimo per inizializzare una variabile, è necessario dichiarare la variabile come locale tipizzata in modo implicito tramite var. Il nome del tipo non può essere specificato nella dichiarazione di variabile, perché solo il compilatore ha accesso al nome sottostante del tipo anonimo. Per altre informazioni su var, vedere Variabili locali tipizzate in modo implicito.

È possibile creare una matrice di elementi tipizzati in modo anonimo combinando una variabile locale tipizzata in modo implicito e una matrice tipizzata in modo implicito, come illustrato nell'esempio seguente.

var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};

I tipi anonimi sono tipi class che derivano direttamente da objecte che non possono essere cast a nessun tipo tranne object. Il compilatore fornisce un nome per qualsiasi tipo anonimo, anche se l'applicazione non può accedervi. Dal punto di vista di Common Language Runtime, un tipo anonimo non è diverso da qualsiasi altro tipo di riferimento.

Se due o più inizializzatori di oggetti anonimi in un assembly specificano una sequenza di proprietà nello stesso ordine e con gli stessi nomi e tipi, il compilatore considera gli oggetti come istanze dello stesso tipo. Condividono le stesse informazioni sul tipo generate dal compilatore.

I tipi anonimi supportano la mutazione non distruttiva sotto forma di con espressioni. In questo modo è possibile creare una nuova istanza di un tipo anonimo in cui una o più proprietà hanno nuovi valori:

var apple = new { Item = "apples", Price = 1.35 };
var onSale = apple with { Price = 0.79 };
Console.WriteLine(apple);
Console.WriteLine(onSale);

Non è possibile dichiarare un campo, una proprietà, un evento o il tipo restituito di un metodo specificando un tipo anonimo. In modo analogo, non è possibile dichiarare un parametro formale di un metodo, una proprietà, un costruttore o un indicizzatore specificando un tipo anonimo. Per passare un tipo anonimo o una raccolta che contiene tipi anonimi, come argomento di un metodo, è possibile dichiarare il parametro come tipo object. Tuttavia, l'uso di object per i tipi anonimi vanifica lo scopo della tipizzazione forte. Se è necessario archiviare i risultati delle query o passarli oltre i limiti del metodo, si consideri l'uso di uno struct o una classe con nome normale invece di un tipo anonimo.

I metodi Equals e GetHashCode nei tipi anonimi sono definiti in termini di metodi delle proprietà Equals e GetHashCode, di conseguenza due istanze dello stesso tipo anonimo sono uguali solo se tutte le relative proprietà sono uguali.

Nota

Il livello di accessibilità di un tipo anonimo è internal, quindi due tipi anonimi definiti in assembly diversi non sono dello stesso tipo. Pertanto, le istanze di tipi anonimi non possono essere uguali tra loro se definite in assembly diversi, anche quando tutte le loro proprietà sono uguali.

I tipi anonimi eseguono l'override del metodo ToString, concatenando il nome e ToString output di ogni proprietà racchiusa tra parentesi graffe.

var v = new { Title = "Hello", Age = 24 };

Console.WriteLine(v.ToString()); // "{ Title = Hello, Age = 24 }"