Agosto 2017

Volume 32 Numero 8

Il presente articolo è stato tradotto automaticamente.

Essential .NET - C# 7.0: Informazioni sulle tuple

Da feed di Mark Michaelis | 2017 agosto | Ottenere il codice

Mark Michaelis
Nella versione di novembre, in Connect (il); problema speciale, fornita una panoramica di c# 7.0 (msdn.microsoft.com/magazine/mt790178), in cui è stata introdotta Tuple. In questo articolo approfondire tuple, che coprono l'intera gamma di opzioni di sintassi.

Per iniziare, si consideri la domanda: Perché le tuple? In alcuni casi, si sarà probabilmente utile combinare gli elementi di dati. Si supponga, ad esempio, che si lavora con le informazioni relative a paesi, ad esempio il paese prestazioni peggiori in tutto il mondo 2017: Malawi, il cui capitale è Lilongwe, con un prodotto interno lordo (PIL) capito di $226.50. È ovviamente possibile dichiarare una classe per questo tipo di dati, ma non ha effettivamente il sostantivo/oggetto comune. È apparentemente più una raccolta di dati correlati che si tratta di un oggetto. Sicuramente, se si prevede di disporre di un oggetto paese, ad esempio, avrebbe notevolmente più dati rispetto alle proprietà solo per il nome, capitale e PIL capite. In alternativa, è possibile archiviare ogni elemento di dati in singole variabili, ma il risultato non sarebbe alcuna associazione tra gli elementi di dati; $226.50 non avrebbe Nessuna associazione con Malawi tranne probabilmente da un suffisso o prefisso comune nei nomi di variabile. Un'altra opzione, è possibile combinare i dati in un'unica stringa, con lo svantaggio che funzionino con ogni elemento dati singolarmente richiedono l'analisi di out. Potrebbe essere un approccio finale per creare un tipo anonimo, ma che, dispone anche di limitazioni. è sufficiente, infatti, che le tuple potenzialmente potrebbero completamente sostituire tipi anonimi. Lasciare in questo argomento fino alla fine dell'articolo.

La scelta migliore potrebbe essere la tupla c# 7.0, che, nella forma più semplice, fornisce una sintassi che consente di combinare l'assegnazione di più variabili, di vari tipi, in un'unica istruzione:

(string country, string capital, double gdpPerCapita) = 
  ("Malawi", "Lilongwe", 226.50);

In questo caso, sono non solo l'assegnazione di più variabili, ma dichiararli anche.

Tuttavia, tuple hanno molte altre sintassi aggiuntiva possibilità, ogni illustrato in figura 1.

Figura 1: codice di esempio per la dichiarazione di tupla e assegnazione

EsempioDescrizioneEsempio di codice
1.Assegnazione di una tupla a variabili dichiarate singolarmente.(paese stringa, capitale stringa, double gdpPerCapita) =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
paese}, {capitale}: {gdpPerCapita} ");
2.Assegnazione di una tupla a variabili dichiarate singolarmente già dichiarate.paese di stringa;
capitale stringa;
Double gdpPerCapita;
(paese, maiuscole, gdpPerCapita) =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
paese}, {capitale}: {gdpPerCapita} ");
3.L'assegnazione di una tupla a singolarmente dichiarati e variabili tipizzate in modo implicito.(paese var var capitale, var gdpPerCapita) =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
paese}, {capitale}: {gdpPerCapita} ");
4.Assegnazione di una tupla a singolarmente dichiarate variabili tipizzate in modo implicito con una sintassi distributiva.var (paese, maiuscole, gdpPerCapita) =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
paese}, {capitale}: {gdpPerCapita} ");
5.Dichiarazione di una tupla di un elemento denominato e assegnazione di valori di tupla e quindi l'accesso gli elementi della tupla in base al nome.(string Name, stringa capitale, double GdpPerCapita) countryInfo =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
countryInfo.Name}, {countryInfo.Capital}: {
countryInfo.GdpPerCapita} ");
6.L'assegnazione di una tupla di un elemento denominato a una singola variabile tipizzata in modo implicito è tipizzata in modo implicito e quindi l'accesso gli elementi della tupla in base al nome.var countryInfo =
(Nome: "Malawi", capitale: "Lilongwe", GdpPerCapita: 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
countryInfo.Name}, {countryInfo.Capital}: {
countryInfo.GdpPerCapita} ");
7.L'assegnazione di una tupla senza nome a una singola variabile tipizzata in modo implicito e quindi l'accesso gli elementi della tupla per la proprietà del numero di articolo.var countryInfo =
("Malawi", "Lilongwe", 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
countryInfo.Item1}, {countryInfo.Item2}: {
countryInfo.Item3} ");
8.L'assegnazione di una tupla di un elemento denominato a una singola variabile tipizzata in modo implicito e quindi l'accesso gli elementi della tupla per la proprietà del numero di articolo.var countryInfo =
(Nome: "Malawi", capitale: "Lilongwe", GdpPerCapita: 226.50);
System.Console.WriteLine (
$@"The paese prestazioni peggiori in tutto il mondo 2017 è {
countryInfo.Item1}, {countryInfo.Item2}: {
countryInfo.Item3} ");
9.Ignorare parti della tupla con caratteri di sottolineatura.(nome della stringa, _, gdpPerCapita double) countryInfo =
("Malawi", "Lilongwe", 226.50);

Nei primi quattro esempi, e anche se il lato destro rappresenta una tupla, la parte sinistra rappresenta comunque le singole variabili che vengono assegnate tra loro tramite sintassi di tupla, che prevede due o più elementi separati da virgole e associati tra parentesi. (Usare la sintassi di tupla termine perché il tipo di dati sottostante, che il compilatore genera l'errore sul lato sinistro non è tecnicamente una tupla.) Il risultato è che anche se inizia con i valori combinati come una tupla a destra, l'assegnazione a sinistra annullamento della costruzione di tupla nelle relative parti costituenti. Nell'esempio 2, l'assegnazione del lato sinistro è per le variabili pre dichiarate. Negli esempi 1, 3 e 4, tuttavia, le variabili vengono dichiarate all'interno di sintassi di tupla. Dato che sono solo dichiarazione di variabili, la denominazione e maiuscole e minuscole convenzione segue indicazioni per la progettazione di Framework generalmente accettati: "usare camelCase per nomi di variabili locali," ad esempio.

Si noti che sebbene tipizzazione implicita (var) possono essere distribuiti in ogni dichiarazione di variabile all'interno di sintassi di tupla, come illustrato nell'esempio 4, si può fare lo stesso con un tipo esplicito (ad esempio stringa). In questo caso, si sta dichiarando effettivamente un tipo di tupla, non solo utilizzando la sintassi di tupla e, pertanto, è necessario aggiungere un riferimento al pacchetto NuGet di System. ValueType, almeno finché .NET 2.0 Standard. Poiché le tuple consentono a ogni elemento deve essere un tipo di dati diversi, la distribuzione il nome di tipo esplicito tra tutti gli elementi necessariamente non funzionerebbe, a meno che tutti i tipi di elemento di dati sono identici (e anche in questo caso, il compilatore non consente).

Nell'esempio 5, I dichiarare una tupla sul lato sinistro e quindi assegnare la tupla sulla destra. Si noti che la tupla è denominato elementi, ovvero i nomi si può quindi fare riferimento per recuperare i valori degli elementi più tupla. Questo è l'elemento che consente la sintassi di countryInfo.GdpPerCapita nell'istruzione System.Console.WriteLine, countryInfo.Capital e countryInfo.Name. Il risultato della dichiarazione di tupla a sinistra è un raggruppamento delle variabili in una singola variabile (countryInfo) da cui è possibile accedere quindi le parti costitutive. Ciò è utile perché è quindi possibile passare questa singola variabile intorno ad altri metodi e i metodi sarà anche in grado di accedere ai singoli elementi all'interno di tupla.

Come già accennato, le variabili definite tramite camelCase utilizzare sintassi di tupla. Tuttavia, la convenzione per i nomi di elemento di tupla non è ben definita. Suggerimenti includono l'utilizzo di convenzioni di denominazione parametro quando la tupla si comporta come un parametro, ad esempio quando la restituzione di più valori prima tupla sintassi verrebbero utilizzati parametri out. L'alternativa consiste nell'utilizzare PascalCase, che seguono la convenzione di denominazione per le proprietà e campi pubblici. Prediligono il secondo approccio sia conforme alle regole di lettere maiuscole/minuscole per gli identificatori (itl.tc/caprfi). I nomi di elemento di tupla vengono visualizzati come membri della tupla e la convenzione per tutti i membri (pubblici) (che sono potenzialmente accessibili tramite un operatore punto) è PascalCase.

Esempio 6 fornisce la stessa funzionalità dell'esempio 5, anche se utilizza gli elementi denominati tupla sul valore di tupla del lato destro, una dichiarazione implicita del tipo a sinistra. I nomi degli elementi sono persistenti, tuttavia, la variabile tipizzata in modo implicito, in modo che siano ancora disponibili per l'istruzione WriteLine. Naturalmente, si aprirà la possibilità che è possibile denominare gli elementi sul lato sinistro con nomi diversi da quelli utilizzati a destra. Anche se il compilatore c# consente questo, verrà generato un avviso che verranno ignorati i nomi degli elementi a destra dopo quelli inclusi nella finestra di sinistra hanno la precedenza.

Se è specificato alcun nome di elemento, i singoli elementi sono ancora disponibili dalla variabile di tupla assegnato. Tuttavia, i nomi sono Item1, Item2 e così via, come illustrato nell'esempio 7. In realtà, il nome ItemX è sempre disponibile in una tupla, anche quando i nomi personalizzati vengono forniti (vedere l'esempio 8). Tuttavia, quando si utilizzano strumenti IDE come con qualsiasi delle versioni recenti di Visual Studio che supportano 7.0 c#, la proprietà ItemX non verrà visualizzate all'interno nell'elenco a discesa IntelliSense, un vantaggio perché presumibilmente il nome fornito è preferibile.

Come illustrato nell'esempio 9, parti di un'assegnazione di tupla possono essere escluso tramite un carattere di sottolineatura; si tratta di un'eliminazione.

Le tuple sono una soluzione semplice per incapsulare i dati in un singolo oggetto nello stesso modo che un contenitore possono essere acquisite vario prelevati dall'archivio. A differenza delle matrici, tuple contengono i tipi di elemento di dati che possono variare virtualmente senza vincolo (anche se non sono consentiti i puntatori), ad eccezione del fatto che è identificati dal codice e non può essere modificati in fase di esecuzione. Inoltre, a differenza di matrici, il numero di elementi nella tupla è hardcoded in fase di compilazione, nonché. Infine, è possibile aggiungere un comportamento personalizzato a una tupla (metodi di estensione nonostante). Se è necessario un comportamento associato ai dati incapsulati, quindi sfruttare orientata agli oggetti programmazione dei e definizione di una classe è la scelta migliore.

Il tipo di System.ValueTuple <>...

Il compilatore c# genera codice che si basa su un set di tipi di valore generici (strutture), ad esempio System.ValueTuple < T1, T2, T3 >, come l'implementazione sottostante per la sintassi di tupla per tutte le istanze di tupla sul lato destro di negli esempi di figura 1. Analogamente, lo stesso set di tipi di valore generici System.ValueTuple <>... viene utilizzato per il tipo di dati lato sinistro a partire da esempio 5. Come previsto con un tipo di tupla, gli unici metodi inclusi sono quelle relative al confronto e uguaglianza. Tuttavia, probabilmente in modo imprevisto, non sono disponibili proprietà per ItemX, ma piuttosto di lettura / scrittura campi (rilievo apparentemente più semplice delle linee guida di programmazione .NET come spiegato in itl.tc/CS7TuplesBreaksGuidelines).

Oltre alla discrepanza linee guida di programmazione, è un'altra domanda comportamento che si verifichi. Dato che i relativi tipi e i nomi di elementi personalizzati non sono inclusi nella definizione di System.ValueTuple <>..., come è possibile che ogni nome di elemento personalizzato apparentemente sia un membro del tipo System.ValueTuple <>... e accedere come membro di quel tipo?

Che cos'è sorprendente (in particolare per coloro che hanno familiarità con l'implementazione del tipo anonimo) è che il compilatore non genera codice Common Intermediate Language (CIL) sottostante per i membri corrispondenti ai nomi personalizzati. Tuttavia, anche in assenza di un membro con il nome personalizzato sottostante è (apparentemente) la prospettiva in c#, tale membro.

Per tutte le tuple denominato locale variabile esempi, ad esempio:

var countryInfo = (Name: "Malawi", Capital: "Lilongwe", GdpPerCapita: 226.50)

è possibile chiaramente che i nomi Impossibile note dal compilatore per il resto dell'ambito della tupla perché tale ambito è limitato all'interno del membro in cui viene dichiarato. E, in realtà, il compilatore e IDE, semplicemente si basano su questo ambito per consentire l'accesso a ogni elemento in base al nome. In altre parole, il compilatore esamina i nomi degli elementi all'interno della dichiarazione di tupla e sfrutta in modo da consentire il codice che utilizza gli stessi nomi all'interno dell'ambito. Per questo motivo, anche che i metodi ItemX non vengono visualizzati in IntelliSense IDE come disponibili membri nella tupla (l'IDE semplicemente li ignora e li sostituisce con gli elementi denominati).

Per determinare i nomi di elemento quando nell'ambito di un membro, è ragionevole per il compilatore, ma cosa accade quando una tupla viene esposta esternamente al membro, ad esempio un parametro o valore restituito da un metodo in un altro assembly (per cui non si è probabilmente codice sorgente disponibile)? Per tutte le tuple che fanno parte dell'API (se un'API pubblica o privata), il compilatore aggiunge i nomi degli elementi ai metadati del membro nella forma di attributi. Ad esempio, il seguente:

[return: System.Runtime.CompilerServices.TupleElementNames(
  new string[] {"First", "Second"})]
public System.ValueTuple<string, string> ParseNames(string fullName)
{
  // ...
}

è l'equivalente c# di ciò che il compilatore genera l'errore per le operazioni seguenti:

public (string First, string Second) ParseNames(string fullName)

Si noti, 7.0 c# non consente l'utilizzo di nomi di elementi personalizzati quando si utilizza il tipo di dati esplicito System.ValueTuple <>.... Pertanto, se si sostituisce la funzione var 8 di esempio di figura 1, verrà generato con avvisi che ogni nome di elemento verrà ignorati.

Ecco alcuni concetti di varie aggiuntive da tenere presenti sulla System.ValueTuple <>...:

  • Sono disponibili un totale di otto strutture System.ValueTuple generiche corrispondente alla possibilità di supportare una tupla con un massimo di sette elementi. Per la tupla ottava, System.ValueTuple < T1, T2, T3, T4, T5, T6, T7, TRest >, l'ultimo parametro di tipo consente di specificare una tupla valore aggiuntivo, consentendo in questo modo il supporto di n elementi. Se, ad esempio, si specifica una tupla con 8 parametri, il compilatore genererà automaticamente un System.ValueTuple < T1, T2, T3, T4, T5, T6, T7, System.ValueTuple < TSub1 >> come tipo di implementazione sottostante. (Per motivi di completezza, System. Value < T1 > esiste, ma saranno effettivamente essere utilizzato solo direttamente e solo come un tipo. Non verrà mai utilizzato direttamente dal compilatore in quanto la sintassi della tupla di c# richiede un minimo di due elementi.)
  • Non vi è un System.ValueTuple non generico che funziona come una factory di tuple con i metodi di creazione corrispondente al grado di tupla ogni valore. La facilità di utilizzo di una tupla letterale, ad esempio t1 var = ("Inigo Montoya", 42), sostituisce almeno il metodo Create per i programmatori di 7.0 (o versioni successive) in c#.
  • Per tutti gli scopi pratici, gli sviluppatori c# possono ignorare essenzialmente System.ValueTuple e System.ValueTuple < T >.

Un altro tipo di tupla che era incluso in .NET Framework 4.5, System. Tuple <>.... In quel momento, è previsto che l'implementazione di tupla core in futuro. Tuttavia, una volta c# supportata la sintassi di tupla, è stato realizzato che un tipo di valore in genere eseguita meglio e in tal caso System.ValueTuple <>... è stato introdotto, sostituendo System. Tuple <>... in tutti i casi, ad eccezione di garantire la compatibilità con le API esistenti che dipendono da System. Tuple <>....

Conclusioni

Ciò che molti utenti non sapevano quando è stata introdotta è che la tupla di c# 7.0 nuovo sostituisce tutto tranne i tipi anonimi e fornisce funzionalità aggiuntive. Tuple possono essere restituite dai metodi, ad esempio, e i nomi dell'elemento vengono rese persistenti nell'API di modo che i nomi significativi possono essere utilizzati al posto dei nomi di tipo ItemX. E, come tipi anonimi, tuple possono anche rappresentare strutture gerarchiche complesse, ad esempio quelli che potrebbe essere costruito nelle query LINQ più complessa (anche se, ad esempio con tipi anonimi, gli sviluppatori devono farlo con attenzione). Ciò premesso, ciò potrebbe causare situazioni in cui il tipo di valore di tupla supera i 128 byte e di conseguenza, potrebbe essere un caso d'angolo per l'utilizzo di tipi anonimi perché è un tipo di riferimento. Ad eccezione di questi casi estremi (accesso tramite reflection tipica potrebbe essere un altro esempio), è quasi per alcun motivo per utilizzare un tipo anonimo durante la programmazione con c# 7.0 o versione successiva.

La possibilità di programma con un oggetto di tipo di tupla è stato rilasciato da molto tempo (come indicato, una classe tupla, System. Tuple <>....., è stata introdotta con .NET Framework 4, ma era disponibile in Silverlight prima di esso). Tuttavia, queste soluzioni mai avuto una sintassi c# associata, ma piuttosto niente di più di un'API .NET. C# 7.0 offre una sintassi di prima classe tupla che consente valori letterali, ad esempio tupla var = (42, "Inigo Montoya"), la tipizzazione implicita, tipizzazione forte, l'utilizzo di API pubblica, IDE supporto integrato per denominata ItemX dati e altro ancora. È vero, potrebbe non essere uno che strumento che in ogni file c#, ma è probabile che un elemento che sarà grato di quando è necessario e si verrà benvenuto la sintassi di tupla sull'alternativa a un parametro o tipo anonimo.

La maggior parte di questo articolo deriva dal mio libro "essenziali c#" (IntelliTect.com/EssentialCSharp), che sono attualmente trova nel mezzo di aggiornamento per "Essenziali c# 7.0." Per ulteriori informazioni su questo argomento, consultare il capitolo 3.

CONVENZIONI DI DENOMINAZIONE PER ELEMENTO DI TUPLA

Eseguire utilizzare camelCase per tutte le variabili dichiarate utilizzando la sintassi di tupla.

Prendere in considerazione utilizzando PascalCase per tutti i nomi di elemento di tupla.


Mark Michaelis* è fondatore di IntelliTect, in cui ha serve come responsabile dell'architettura tecnica e trainer. Per quasi due decenni è stato MVP di Microsoft e un direttore regionale Microsoft dal 2007. Michaelis serve più Microsoft software revisione team di progettazione, ad esempio c#, Microsoft Azure, SharePoint e Visual Studio ALM. Egli legge conferenze developer e scritto numerosi libri, tra cui la più recente, "essenziali c# 6.0 (Edition 5)" (itl.tc/EssentialCSharp). È possibile contattarlo su Facebook al facebook.com/Mark.Michaelis, nel suo blog al IntelliTect.com/Mark, su Twitter: @markmichaelis o tramite posta elettronica in mark@IntelliTect.com.*

Grazie per il seguente esperto tecnico di Microsoft per la revisione dell'articolo: Mads Torgersen


Viene illustrato in questo articolo nel forum di MSDN Magazine