Tipi anonimi (Visual Basic)

Visual Basic supporta i tipi anonimi, che consentono di creare oggetti senza scrivere una definizione di classe per il tipo di dati. La classe viene generata direttamente dal compilatore. La classe non ha un nome utilizzabile, eredita direttamente da Object e contiene le proprietà specificate nella dichiarazione dell'oggetto. Poiché il nome del tipo non è specificato, viene definito tipo anonimo.

Nell'esempio seguente viene dichiarata e creata una variabile product come istanza di un tipo anonimo che ha due proprietà Name e Price.

' Variable product is an instance of a simple anonymous type.
Dim product = New With {Key .Name = "paperclips", .Price = 1.29}

Un'espressione di query usa tipi anonimi per combinare colonne di dati selezionate da una query. Non è possibile definire il tipo di risultato in anticipo, perché non è possibile prevedere le colonne selezionate da una query specifica. I tipi anonimi consentono di scrivere una query che seleziona un numero qualsiasi di colonne, in qualunque ordine. Il compilatore crea un tipo di dati che corrisponde alle proprietà specificate e all'ordine specificato.

Negli esempi seguenti è products un elenco di oggetti prodotto, ognuno dei quali ha molte proprietà. La variabile namePriceQuery contiene la definizione di una query che, quando eseguita, restituisce una raccolta di istanze di un tipo anonimo che ha due proprietà, Name e Price.

Dim namePriceQuery = From prod In products
                     Select prod.Name, prod.Price

La variabile nameQuantityQuery contiene la definizione di una query che, quando eseguita, restituisce una raccolta di istanze di un tipo anonimo che ha due proprietà, Name e OnHand.

Dim nameQuantityQuery = From prod In products
                        Select prod.Name, prod.OnHand

Per altre informazioni sul codice creato dal compilatore per un tipo anonimo, vedere Definizione di tipo anonimo.

Attenzione

Il nome del tipo anonimo è generato dal compilatore e può variare da compilazione a compilazione. Il codice non deve usare o basarsi sul nome di un tipo anonimo perché il nome potrebbe cambiare quando un progetto viene ricompilato.

Dichiarazione di un tipo anonimo

La dichiarazione di un'istanza di un tipo anonimo usa un elenco di inizializzatori per specificare le proprietà del tipo. È possibile specificare solo le proprietà quando si dichiara un tipo anonimo, non altri elementi della classe, ad esempio metodi o eventi. Nell'esempio seguente product1 è un'istanza di un tipo anonimo che ha due proprietà: Name e Price.

' Variable product1 is an instance of a simple anonymous type.
Dim product1 = New With {.Name = "paperclips", .Price = 1.29}
' -or-
' product2 is an instance of an anonymous type with key properties.
Dim product2 = New With {Key .Name = "paperclips", Key .Price = 1.29}

Se si designano le proprietà come proprietà chiave, è possibile usarle per confrontare due istanze di tipo anonimo per verificarne l'uguaglianza. Tuttavia, non è possibile modificare i valori delle proprietà chiave. Per altre informazioni, vedere la sezione Proprietà chiave più avanti in questo argomento.

Si noti che la dichiarazione di un'istanza di un tipo anonimo è simile alla dichiarazione di un'istanza di un tipo denominato tramite un inizializzatore di oggetto:

' Variable product3 is an instance of a class named Product.
Dim product3 = New Product With {.Name = "paperclips", .Price = 1.29}

Per altre informazioni su altri modi per specificare le proprietà dei tipi anonimi, vedere Procedura: Dedurre tipi e nomi di proprietà nelle dichiarazioni di tipo anonimo.

Proprietà chiave

Le proprietà chiave differiscono dalle proprietà non chiave per diversi aspetti fondamentali:

  • Vengono confrontati solo i valori delle proprietà chiave per determinare se due istanze sono uguali.

  • I valori delle proprietà chiave sono di sola lettura e non possono essere modificati.

  • Solo i valori delle proprietà chiave sono inclusi nell'algoritmo di codice hash generato dal compilatore per un tipo anonimo.

Equality

Le istanze di tipi anonimi possono essere uguali solo se sono istanze dello stesso tipo anonimo. Il compilatore considera due istanze come istanze dello stesso tipo se soddisfano le condizioni seguenti:

  • Sono dichiarate nello stesso assembly.

  • Le relative proprietà hanno gli stessi nomi, gli stessi tipi dedotti e sono dichiarate nello stesso ordine. I confronti dei nomi non fanno distinzione tra maiuscole e minuscole.

  • Le stesse proprietà in ogni istanza sono contrassegnate come proprietà chiave.

  • Almeno una proprietà in ogni dichiarazione è una proprietà chiave.

Un'istanza di un tipo anonimo che non ha proprietà chiave è uguale solo a se stessa.

' prod1 and prod2 have no key values.
Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}

' The following line displays False, because prod1 and prod2 have no
' key properties.
Console.WriteLine(prod1.Equals(prod2))

' The following statement displays True because prod1 is equal to itself.
Console.WriteLine(prod1.Equals(prod1))

Due istanze dello stesso tipo anonimo sono uguali se i valori delle relative proprietà della chiave sono uguali. Gli esempi seguenti illustrano come viene testata l'uguaglianza.

Dim prod3 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", Key .Price = 1.29}
' The following line displays True, because prod3 and prod4 are
' instances of the same anonymous type, and the values of their
' key properties are equal.
Console.WriteLine(prod3.Equals(prod4))

Dim prod5 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim prod6 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays False, because prod5 and prod6 do not 
' have the same properties.
Console.WriteLine(prod5.Equals(prod6))

Dim prod7 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 24}
Dim prod8 = New With {Key .Name = "paperclips", Key .Price = 1.29,
                      .OnHand = 423}
' The following line displays True, because prod7 and prod8 are
' instances of the same anonymous type, and the values of their
' key properties are equal. The equality check does not compare the
' values of the non-key field.
Console.WriteLine(prod7.Equals(prod8))

Valori di sola lettura

Non è possibile modificare i valori delle proprietà chiave. Ad esempio, in prod8 nell'esempio precedente, i campi Name e Price sono read-only, ma è possibile modificare OnHand.

' The following statement will not compile, because Name is a key
' property and its value cannot be changed.
' prod8.Name = "clamps"

' OnHand is not a Key property. Its value can be changed.
prod8.OnHand = 22

Tipi anonimi da espressioni di query

Le espressioni di query non richiedono sempre la creazione di tipi anonimi. Quando possibile, usano un tipo esistente per contenere i dati della colonna. Ciò si verifica quando la query restituisce record interi dall'origine dati o un solo campo da ogni record. Negli esempi di codice seguenti customers è una raccolta di oggetti di una classe Customer. La classe ha molte proprietà ed è possibile includerne una o più nel risultato della query, in qualsiasi ordine. Nei primi due esempi non sono necessari tipi anonimi perché le query selezionano elementi di tipi denominati:

  • custs1 contiene una raccolta di stringhe, perché cust.Name è una stringa.

    Dim custs1 = From cust In customers
                 Select cust.Name
    
  • custs2 contiene una raccolta di oggetti Customer, perché ogni elemento di customers è un oggetto Customer e l'intero elemento viene selezionato dalla query.

    Dim custs2 = From cust In customers
                 Select cust
    

Tuttavia, i tipi denominati appropriati non sono sempre disponibili. È possibile selezionare i nomi e gli indirizzi dei clienti per uno scopo, i numeri ID cliente e le località per un altro, nonché i nomi dei clienti, gli indirizzi e le cronologie degli ordini per un terzo. I tipi anonimi consentono di selezionare qualsiasi combinazione di proprietà, in qualunque ordine, senza prima dichiarare un nuovo tipo denominato per contenere il risultato. Il compilatore crea invece un tipo anonimo per ogni compilazione di proprietà. La query seguente seleziona solo il nome e il numero ID del cliente da ogni oggetto Customer in customers. Di conseguenza, il compilatore crea un tipo anonimo che contiene solo queste due proprietà.

Dim custs3 = From cust In customers
             Select cust.Name, cust.ID

Sia i nomi sia i tipi di dati delle proprietà nel tipo anonimo derivano dagli argomenti per Select, cust.Name e cust.ID. Le proprietà in un tipo anonimo creato da una query sono sempre proprietà chiave. Quando si esegue custs3 nel ciclo For Each seguente, il risultato è una raccolta di istanze di un tipo anonimo con due proprietà chiave, Name e ID.

For Each selectedCust In custs3
    Console.WriteLine(selectedCust.ID & ": " & selectedCust.Name)
Next

Gli elementi della raccolta rappresentati da custs3 sono fortemente tipizzati ed è possibile usare IntelliSense per spostarsi tra le proprietà disponibili e per verificarne i tipi.

Per altre informazioni, vedere Introduzione a LINQ in Visual Basic.

Decidere se usare tipi anonimi o meno

Prima di creare un oggetto come istanza di una classe anonima, valutare se si tratta dell'opzione migliore. Ad esempio, se si vuole creare un oggetto temporaneo per contenere dati correlati e non sono necessari altri campi e metodi che una classe completa potrebbe contenere, un tipo anonimo è una soluzione valida. I tipi anonimi sono utili anche se si desidera una selezione diversa di proprietà per ogni dichiarazione o se si desidera modificare l'ordine delle proprietà. Tuttavia, se il progetto include numerosi oggetti con le stesse proprietà in un ordine fisso, è possibile dichiararli più facilmente usando un tipo denominato con un costruttore di classe. Ad esempio, con un costruttore appropriato, è più facile dichiarare più istanze di una classe Product anziché dichiarare numerose istanze di un tipo anonimo.

' Declaring instances of a named type.
Dim firstProd1 As New Product("paperclips", 1.29)
Dim secondProd1 As New Product("desklamp", 28.99)
Dim thirdProd1 As New Product("stapler", 5.09)

' Declaring instances of an anonymous type.
Dim firstProd2 = New With {Key .Name = "paperclips", Key .Price = 1.29}
Dim secondProd2 = New With {Key .Name = "desklamp", Key .Price = 28.99}
Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}

Un altro vantaggio dei tipi denominati è che il compilatore può rilevare un errore di digitazione accidentale di un nome di proprietà. Negli esempi precedenti, firstProd2, secondProd2 e thirdProd2 sono destinati a essere istanze dello stesso tipo anonimo. Tuttavia, se si dovesse dichiarare thirdProd2 accidentalmente in uno dei modi seguenti, il relativo tipo sarebbe diverso da quello di firstProd2 e secondProd2.

' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = 5.09}
' Dim thirdProd2 = New With {Key .Name = "stapler", Key .Price = "5.09"}
' Dim thirdProd2 = New With {Key .Name = "stapler", .Price = 5.09}

Inoltre, esistono limitazioni sull'uso di tipi anonimi che non si applicano alle istanze di tipi denominati. firstProd2, secondProd2 e thirdProd2 sono istanze dello stesso tipo anonimo. Tuttavia, il nome per il tipo anonimo condiviso non è disponibile e non può essere visualizzato dove è previsto un nome di tipo nel codice. Ad esempio, non è possibile usare un tipo anonimo per definire una firma del metodo, dichiarare un'altra variabile o campo o in qualsiasi dichiarazione di tipo. Di conseguenza, i tipi anonimi non sono appropriati quando è necessario condividere informazioni tra metodi.

Definizione di tipo anonimo

In risposta alla dichiarazione di un'istanza di un tipo anonimo, il compilatore crea una nuova definizione di classe contenente le proprietà specificate.

Se il tipo anonimo contiene almeno una proprietà chiave, la definizione esegue l'override dei tre membri ereditati da Object: Equals, GetHashCode e ToString. Il codice generato per testare l'uguaglianza e determinare il valore del codice hash considera solo le proprietà chiave. Se il tipo anonimo non contiene proprietà chiave, viene eseguito l'override solo di ToString. Le proprietà denominate in modo esplicito di un tipo anonimo non possono essere in conflitto con questi metodi generati. Ciò significa che non è possibile usare .Equals, .GetHashCode o .ToString per assegnare un nome a una proprietà.

Le definizioni di tipi anonimi che includono almeno una proprietà chiave implementano anche l'interfaccia System.IEquatable<T>, dove T è il tipo del tipo anonimo.

Per altre informazioni sul codice creato dal compilatore e sulla funzionalità dei metodi sottoposti a override, vedere Definizione di tipo anonimo.

Vedi anche