Notación literal para Diccionario en C #?

182

Actualmente tengo un WebSocket entre JavaScript y un servidor programado en C #. En JavaScript, puedo pasar datos fácilmente usando una matriz asociativa:

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

Para representar este objeto de datos en el lado del servidor, utilizo a Dictionary<string, string>, pero esto es más 'costoso al escribir' que en JavaScript:

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

¿Hay algún tipo de notación literal para matrices / Dictionarys asociativas en C #?

pimvdb
fuente
C # 9 contiene una propuesta para Dictionary Literals. Publico una respuesta al respecto a continuación . Nuestro sueño hecho realidad!
aloisdg se muda a codidact.com

Respuestas:

291

Utiliza la sintaxis del inicializador de colección , pero aún necesita hacer un new Dictionary<string, string>objeto primero ya que la sintaxis de acceso directo se traduce en un montón de Add()llamadas (como su código):

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

En C # 6, ahora tiene la opción de usar una sintaxis más intuitiva con Dictionary, así como con cualquier otro tipo que admita indexadores . La declaración anterior se puede reescribir como:

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

A diferencia de los inicializadores de colección, esto invoca el setter indexador debajo del capó, en lugar de un Add()método apropiado .

BoltClock
fuente
2
Gracias. ¿Es posible eliminar uno de los Dictionary<string, string>sin embargo? Parece bastante redundante, pero podría estar equivocado. Editar: Esta parece una forma más preferible, gracias.
pimvdb
3
@pimvdb: Sí, puedes: declararlo como a var, el compilador inferirá el tipo de new. Edité mi respuesta.
BoltClock
77
Tenga en cuenta que no es una notación literal, estrictamente hablando ... es solo un atajo para la inicialización. Solo las cadenas y algunos tipos primitivos tienen una representación literal
Thomas Levesque
Si quiere decir que desea eliminar una de las "cadenas", no son redundantes, una es para el tipo de clave y la otra para el tipo de valor. No existe un literal específico para los diccionarios, la notación utilizada en esta respuesta es general para todas las clases con un método Add que toma dos cadenas (e implementa IEnumerable).
Markus Johnsson
1
@ Markus Johnsson: Se refería, literalmente, a Dictionary<string, string>. Mi código originalmente declaró el tipo, acababa de cambiarlo vardespués de su comentario.
BoltClock
13

Si bien, la respuesta del inicializador del diccionario es totalmente correcta, hay otro enfoque que señalaría (pero podría no recomendarlo). Si su objetivo es proporcionar un uso de API conciso, puede usar objetos anónimos.

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

La variable "datos" es entonces de un tipo anónimo "indescriptible", por lo que solo puede pasar esto como System.Object. Luego, podría escribir código que pueda transformar un objeto anónimo en un diccionario. Tal código dependería de la reflexión, que potencialmente sería lenta. Sin embargo, podría usar System.Reflection.Emit, o System.Linq.Expressionspara compilar y almacenar en caché un delegado que haría llamadas posteriores mucho más rápido.

Las API de MVC de Asp.net utilizan esta técnica en varios lugares que he visto. Muchos de los Helt Html tienen sobrecargas que aceptan un objeto o un diccionario. Supongo que el objetivo de su diseño de API es el mismo que el que buscas; sintaxis breve en la llamada al método.

MarkPflug
fuente
1
Eso solo ayudará cuando el conjunto de claves sea estático. También puedes usar Tuples para el mismo propósito.
sehe
8

Utilizando DynamicObject, no es tan difícil crear un inicializador de diccionario más simple.

Imagina que quieres llamar al siguiente método

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

usando una sintaxis literal como

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

Esto se puede lograr creando un 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
fuente
6

Usar Diccionario Literales (propuesta C # 9)

C # 9 introduce una sintaxis más simple para crear Dictionary<TKey,TValue>objetos inicializados sin tener que especificar ni el nombre de tipo Diccionario ni los parámetros de tipo. Los parámetros de tipo para el diccionario se infieren utilizando las reglas existentes utilizadas para la inferencia 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];  

Este Synthax simplifica el trabajo con diccionarios en C # y elimina el código redundante.

Puede seguir el problema en GitHub (y aquí está el hito para C # 9 ).

Editar: esta propuesta se rechaza actualmente :

[...] Creemos que hay varios casos de uso interesantes en torno a la inicialización de datos, particularmente para cosas como diccionarios inmutables. No encontramos la sintaxis existente para inicializar un diccionario tan onerosa, ni la vemos como un patrón frecuente en el código que se beneficiaría mucho de una función de lenguaje. Creemos que el área general de inicialización de datos debe analizarse nuevamente después de hacer registros y marchitarse. [...]

aloisdg se muda a codidact.com
fuente
1
Gran idea, espero que lo
logre