Notation littérale pour Dictionary en C #?

182

J'ai actuellement un WebSocket entre JavaScript et un serveur programmé en C #. En JavaScript, je peux facilement transmettre des données à l'aide d'un tableau associatif:

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

Pour représenter cet objet de données côté serveur, j'utilise a Dictionary<string, string>, mais cela coûte plus cher à la saisie qu'en JavaScript:

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

Existe-t-il une sorte de notation littérale pour les tableaux / Dictionarys associatifs en C #?

pimvdb
la source
C # 9 contient une proposition pour Dictionary Literals. Je poste une réponse à ce sujet ci-dessous . Notre rêve devient réalité!
aloisdg passe à codidact.com

Réponses:

291

Vous utilisez la syntaxe d' initialisation de collection , mais vous devez toujours créer un new Dictionary<string, string>objet en premier car la syntaxe de raccourci est traduite en un tas d' Add()appels (comme votre code):

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

En C # 6, vous avez désormais la possibilité d'utiliser une syntaxe plus intuitive avec Dictionary ainsi qu'avec tout autre type prenant en charge les indexeurs . La déclaration ci-dessus peut être réécrite comme suit:

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

Contrairement aux initialiseurs de collection, cela appelle le setter d'indexeur sous le capot, plutôt qu'une Add()méthode appropriée .

BoltClock
la source
2
Merci. Est-il possible de supprimer l'un des Dictionary<string, string>si? Cela semble plutôt redondant, mais je me trompe peut-être. Edit: Cela semble en effet une manière plus préférable, merci.
pimvdb
3
@pimvdb: Oui, vous pouvez: le déclarer comme un var, le compilateur déduira le type à partir du fichier new. J'ai édité ma réponse.
BoltClock
7
Notez que ce n'est pas une notation littérale, à proprement parler ... c'est juste un raccourci pour l'initialisation. Seules les chaînes et certains types primitifs ont une représentation littérale
Thomas Levesque
Si vous voulez supprimer l'une des "chaînes" - elles ne sont pas redondantes - l'une est pour le type de clé, l'autre pour le type valeur. Il n'y a pas de littéral spécifique pour les dictionnaires, la notation utilisée dans cette réponse est générale pour toutes les classes avec une méthode Add qui prend deux chaînes (et implémente IEnumerable).
Markus Johnsson
1
@Markus Johnsson: Il voulait dire, littéralement, Dictionary<string, string>. Mon code avait initialement déclaré le type, je venais de le changer varaprès son commentaire.
BoltClock
13

Bien que la réponse de l'initialisation du dictionnaire soit totalement correcte, il existe une autre approche que je voudrais souligner (mais je ne la recommanderais peut-être pas). Si votre objectif est de fournir une utilisation brève de l'API, vous pouvez utiliser des objets anonymes.

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

La variable "data" est alors d'un type anonyme "indescriptible", vous ne pouvez donc la passer qu'en tant que System.Object. Vous pouvez alors écrire du code qui peut transformer un objet anonyme en dictionnaire. Un tel code reposerait sur une réflexion, qui serait potentiellement lente. Cependant, vous pouvez utiliser System.Reflection.Emit, ou System.Linq.Expressionspour compiler et mettre en cache un délégué qui effectuerait les appels ultérieurs beaucoup plus rapidement.

Les API Asp.net MVC utilisent cette technique dans un certain nombre d'endroits que j'ai vus. De nombreux Helpers Html ont des surcharges qui acceptent un objet ou un dictionnaire. Je suppose que l'objectif de leur conception d'API est le même que celui que vous recherchez; syntaxe concise lors de l'appel de méthode.

MarkPflug
la source
1
Cela n'aidera que lorsque l'ensemble de clés est statique. Vous pouvez également utiliser Tuples dans le même but.
sehe
8

En utilisant DynamicObject, il n'est pas si difficile de créer un initialiseur de dictionnaire plus simple.

Imaginez que vous vouliez appeler la méthode suivante

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

en utilisant une syntaxe littérale comme

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

Cela peut être accompli en créant un objet dynamique comme celui-ci

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;
    }
}
Krumelur
la source
6

Utiliser les littéraux de dictionnaire (proposition C # 9)

C # 9 introduit une syntaxe plus simple pour créer des Dictionary<TKey,TValue>objets initialisés sans avoir à spécifier le nom du type Dictionary ou les paramètres de type. Les paramètres de type du dictionnaire sont déduits à l'aide des règles existantes utilisées pour l'inférence de type de tableau.

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

Cette syntaxe simplifie le travail avec les dictionnaires en C # et supprime le code redondant.

Vous pouvez suivre le problème sur GitHub (et voici le jalon pour C # 9 ).

Modifier: Cette proposition est actuellement rejetée :

[...] Nous pensons qu'il existe un certain nombre de cas d'utilisation intéressants autour de l'initialisation des données, en particulier pour des choses comme les dictionnaires immuables. Nous ne trouvons pas la syntaxe existante pour initialiser un dictionnaire si onéreuse, et nous ne la voyons pas non plus comme un modèle fréquent dans le code qui bénéficierait beaucoup d'une fonctionnalité de langage. Nous pensons que le domaine général de l'initialisation des données devrait être réexaminé après avoir fait des enregistrements et se faner. [...]

aloisdg passe à codidact.com
la source
1
Bonne idée, j'espère qu'elle
arrivera