Notação literal para dicionário em c #?

182

Atualmente, tenho um WebSocket entre JavaScript e um servidor programado em C #. Em JavaScript, eu posso transmitir dados facilmente usando uma matriz associativa:

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

Para representar esse objeto de dados no lado do servidor, eu uso a Dictionary<string, string>, mas isso é mais "caro para digitar" do que no JavaScript:

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

Existe algum tipo de notação literal para matrizes / Dictionarys associativas em C #?

pimvdb
fonte
O C # 9 contém uma proposta para literais de dicionário. Eu posto uma resposta sobre isso abaixo . Nosso sonho tornado realidade!
aloisdg movendo-se para codidact.com

Respostas:

291

Você usa a sintaxe do inicializador de coleção , mas ainda precisa criar um new Dictionary<string, string>objeto, pois a sintaxe do atalho é traduzida para várias Add()chamadas (como o seu código):

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

No C # 6, agora você tem a opção de usar uma sintaxe mais intuitiva com o Dictionary e com qualquer outro tipo que suporte indexadores . A declaração acima pode ser reescrita como:

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

Diferentemente dos inicializadores de coleção, isso chama o indexador sob o capô, em vez de um Add()método apropriado .

BoltClock
fonte
2
Obrigado. É possível remover um dos Dictionary<string, string>itens? Parece bastante redundante, mas posso estar errado. Edit: Esta parece uma maneira mais preferível, de fato, obrigado.
Pimvdb
3
@ pimvdb: Sim, você pode: declarar como um var, o compilador inferirá o tipo do new. Eu editei minha resposta.
BoltClock
7
Observe que não é uma notação literal, a rigor ... é apenas um atalho para inicialização. Apenas corda e alguns tipos primitivos têm uma representação literal
Thomas Levesque
Se você quer dizer que deseja remover uma das "cadeias" - elas não são redundantes - uma é para o tipo de chave e a outra para o tipo de valor. Não há literal específico para Dicionários, a notação usada nesta resposta é geral para todas as classes com um método Add que utiliza duas strings (e implementa IEnumerable).
Markus Johnsson
1
@ Markus Johnsson: Ele quis dizer, literalmente Dictionary<string, string>,. Meu código originalmente declarou o tipo, eu havia mudado para vardepois do comentário dele.
BoltClock
13

Embora a resposta do inicializador de dicionário esteja totalmente correta, há outra abordagem que eu indicaria (mas talvez eu não a recomende). Se seu objetivo é fornecer uso conciso da API, você pode usar objetos anônimos.

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

A variável "data" é então de um tipo anônimo "indizível", então você só pode passar isso como System.Object. Você pode escrever um código que pode transformar um objeto anônimo em um dicionário. Esse código dependeria da reflexão, o que potencialmente seria lento. No entanto, você pode usar System.Reflection.Emitou System.Linq.Expressionscompilar e armazenar em cache um delegado que tornaria as chamadas subseqüentes muito mais rápidas.

As APIs do MVC do Asp.net usam essa técnica em vários lugares que eu já vi. Muitos Assistentes de HTML têm sobrecargas que aceitam um objeto ou um dicionário. Presumo que o objetivo do design da API seja o mesmo que você procura; sintaxe concisa na chamada do método.

MarkPflug
fonte
1
Isso só vai ajudar quando o conjunto de chaves é estático. Você também pode usar tuplas para a mesma finalidade.
sehe
8

Usando DynamicObject, não é tão difícil criar um inicializador de dicionário mais simples.

Imagine que você deseja chamar o seguinte método

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

usando uma sintaxe literal como

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

Isso pode ser conseguido criando um objeto dinâmico como este

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
fonte
6

Usar literais do dicionário (proposta C # 9)

O C # 9 apresenta uma sintaxe mais simples para criar Dictionary<TKey,TValue>objetos inicializados sem precisar especificar o nome do tipo Dicionário ou os parâmetros de tipo. Os parâmetros de tipo para o dicionário são inferidos usando as regras existentes usadas para inferência de tipo de matriz.

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

Essa sintaxe simplifica o trabalho com dicionários em C # e remove o código redundante.

Você pode acompanhar o problema no GitHub (e aqui está o marco do C # 9 ).

Editar: esta proposta está atualmente rejeitada :

[...] Acreditamos que existem vários casos de uso interessantes em torno da inicialização de dados, particularmente para itens como dicionários imutáveis. Não encontramos a sintaxe existente para inicializar um dicionário tão onerosa, nem a vemos como um padrão frequente no código que se beneficiaria muito com um recurso de idioma. Concluímos que a área geral de inicialização de dados deve ser analisada novamente depois de fazermos registros e murchar. [...]

aloisdg movendo-se para codidact.com
fonte
1
Grande ideia, esperança torna-in, parece como um acéfalo
jjxtra