Notazione letterale per Dictionary in C #?


182

Al momento ho un WebSocket tra JavaScript e un server programmato in C #. In JavaScript, posso passare facilmente i dati usando un array associativo:

var data = {'test': 'val',
            'test2': 'val2'};

Per rappresentare questo oggetto dati sul lato server, uso a Dictionary<string, string>, ma questo è più "costoso da scrivere" che in JavaScript:

Dictionary<string, string> data = new Dictionary<string,string>();
data.Add("test", "val");
data.Add("test2", "val2");

Esiste una sorta di notazione letterale per array / Dictionaryi associativi in C #?


C # 9 contiene una proposta per dizionari letterali. Pubblico una risposta al riguardo di seguito . Il nostro sogno diventa realtà!
aloisdg si trasferisce su codidact.com

Risposte:


291

Si utilizza la sintassi dell'inizializzatore di raccolta , ma è comunque necessario creare new Dictionary<string, string>prima un oggetto poiché la sintassi del collegamento viene tradotta in un gruppo di Add()chiamate (come il codice):

var data = new Dictionary<string, string>
{
    { "test", "val" }, 
    { "test2", "val2" }
};

In C # 6, ora hai la possibilità di utilizzare una sintassi più intuitiva con Dizionario e qualsiasi altro tipo che supporti gli indicizzatori . L'istruzione precedente può essere riscritta come:

var data = new Dictionary<string, string>
{
    ["test"] = "val",
    ["test2"] = "val2"
};

A differenza degli inizializzatori di raccolta, questo richiama il setter indicizzatore sotto il cofano, piuttosto che un Add()metodo appropriato .


2
Grazie. È possibile rimuovere uno dei due Dictionary<string, string>? Sembra piuttosto ridondante, ma potrei sbagliarmi. Modifica: questo sembra davvero un modo preferibile, grazie.
pimvdb,

3
@pimvdb: Sì, puoi: dichiararlo come a var, il compilatore inferirà il tipo da new. Ho modificato la mia risposta.
BoltClock

7
Nota che non è una notazione letterale, a rigor di termini ... è solo una scorciatoia per l'inizializzazione. Solo la stringa e alcuni tipi primitivi hanno una rappresentazione letterale
Thomas Levesque,

Se vuoi dire che vuoi rimuovere una delle "stringhe" - non sono ridondanti - una è per il tipo di chiave, l'altra per il tipo di valore. Non esiste uno specifico letterale per i dizionari, la notazione utilizzata in questa risposta è generale per tutte le classi con un metodo Add che accetta due stringhe (e implementa IEnumerable).
Markus Johnsson,

1
@Markus Johnsson: intendeva letteralmente Dictionary<string, string>. Il mio codice inizialmente ha dichiarato il tipo, l'ho appena cambiato vardopo il suo commento.
BoltClock

13

Mentre la risposta all'inizializzatore del dizionario è totalmente corretta, c'è un altro approccio che vorrei sottolineare (ma potrei non consigliarlo). Se il tuo obiettivo è fornire un utilizzo dell'API conciso, puoi utilizzare oggetti anonimi.

var data = new { test1 = "val", test2 = "val2"};

La variabile "data" è quindi di tipo anonimo "indicibile", quindi puoi solo passare questo System.Object. È quindi possibile scrivere codice in grado di trasformare un oggetto anonimo in un dizionario. Tale codice si baserebbe sulla riflessione, che sarebbe potenzialmente lenta. Tuttavia, è possibile utilizzare System.Reflection.Emito System.Linq.Expressionscompilare e memorizzare nella cache un delegato che renderebbe le chiamate successive molto più veloci.

Le API Asp.net MVC usano questa tecnica in diversi posti che ho visto. Molti degli helper HTML hanno sovraccarichi che accettano un oggetto o un dizionario. Presumo che l'obiettivo della loro progettazione API sia lo stesso di quello che stai cercando; sintassi sintetica alla chiamata del metodo.


1
Ciò sarà di aiuto solo quando l'insieme di chiavi è statico. Puoi anche usare Tuple per lo stesso scopo.
visto il

8

Utilizzando DynamicObject, non è così difficile creare un inizializzatore di dizionario più semplice.

Immagina di voler chiamare il seguente metodo

void PrintDict(IDictionary<string, object> dict) {
    foreach(var kv in dict) {
        Console.WriteLine ("  -> " + kv.Key + " = " + kv.Value);
    }
}

usando una sintassi letterale come

var dict = Dict (Hello: "World", IAm: "a dictionary");
PrintDict (dict);

Ciò può essere realizzato creando un oggetto dinamico come questo

dynamic Dict {
    get {
        return new DynamicDictFactory ();
    }
}

private class DynamicDictFactory : DynamicObject
{
    public override bool TryInvoke (InvokeBinder binder, object[] args, out object result)
    {
        var res = new Dictionary<string, object> ();
        var names = binder.CallInfo.ArgumentNames;

        for (var i = 0; i < args.Length; i++) {
            var argName = names [i];
            if(string.IsNullOrEmpty(argName)) throw new ArgumentException();
            res [argName] = args [i];
        }
        result = res;
        return true;
    }
}

6

Usa dizionari letterali (proposta C # 9)

C # 9 introduce una sintassi più semplice per creare Dictionary<TKey,TValue>oggetti inizializzati senza dover specificare il nome del tipo di dizionario oi parametri del tipo. I parametri di tipo per il dizionario vengono dedotti utilizzando le regole esistenti utilizzate per l'inferenza del tipo di array.

// C# 1..8    
var x = new Dictionary <string,int> () { { "foo", 4 }, { "bar", 5 }};   
// C# 9    
var x = ["foo":4, "bar": 5];  

Questa sintassi semplifica il lavoro con i dizionari in C # e rimuove il codice ridondante.

Puoi seguire il problema su GitHub (e qui è la pietra miliare per C # 9 ).

Modifica: questa proposta è attualmente respinta :

[...] Pensiamo che ci siano una serie di casi d'uso interessanti sull'inizializzazione dei dati, in particolare per cose come dizionari immutabili. Non troviamo la sintassi esistente per inizializzare un dizionario così oneroso, né lo vediamo come un modello frequente nel codice che trarrebbe molto beneficio da una funzionalità linguistica. Pensiamo che l'area generale di inizializzazione dei dati dovrebbe essere esaminata di nuovo dopo aver fatto record e appassimento. [...]


1
Ottima idea, spero che
arrivi
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.