2012-06-24 19 views
20

¿Por qué es posible inicializar una Dictionary<T1,T2> así:Cómo hacer que la inicialización de matriz en línea funcione, como p. ¿Inicialización del diccionario?

var dict = new Dictionary<string,int>() { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

... pero no para inicializar, por ejemplo, una serie de objetos KeyValuePair<T1,T2> exactamente de la misma manera:

var kvps = new KeyValuePair<string,int>[] { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 
// compiler error: "Array initializers can only be used in a variable 
// or field initializer. Try using a new expression instead." 

que se dan cuenta que podría hacer que el segundo ejemplo funcione simplemente escribiendo new KeyValuePair<string,int>() { "key1", 1 }, etc. para cada elemento. Pero me pregunto si es posible usar el mismo tipo de sintaxis concisa que es posible en el primer ejemplo.

Si no es posible, entonces ¿qué hace que el Diccionario tipo tan especial?

+0

me gusta hacia dónde va esta pregunta, pero parece sin refinar. Me gusta la idea de centrarme en 'List' vs.' Dictionary' (para sacar el Array del camino), aunque el mensaje de error podría ser diferente. (Musing:?. ¿Cómo se sabe cómo crear un KVP de '{a, b}' ;-) –

+0

(no creo que me dieron el título del todo bien, sin embargo) –

+0

@pst sí, creo que' me estoy acercando al núcleo de lo que estoy preguntando. No soy muy bueno para escribir preguntas ... – McGarnagle

Respuesta

33

La sintaxis de inicializador de colección se traduce en llamadas a Add con el número apropiado de parámetros:

var dict = new Dictionary<string,int>(); 
dict.Add("key1", 1); 
dict.Add("key2", 2); 

Esta sintaxis especial inicializador también trabajará en otras clases que tienen un método Add e implementa IEnumerable. Vamos a crear una clase completamente loco sólo para demostrar que no hay nada de especial en Dictionary y que esta sintaxis puede funcionar para cualquier clase adecuada:

// Don't do this in production code! 
class CrazyAdd : IEnumerable 
{ 
    public void Add(int x, int y, int z) 
    { 
     Console.WriteLine(x + y + z); // Well it *does* add... 
    } 

    public IEnumerator GetEnumerator() { throw new NotImplementedException(); } 
} 

Ahora usted puede escribir lo siguiente:

var crazyAdd = new CrazyAdd 
{ 
    {1, 2, 3}, 
    {4, 5, 6} 
}; 

Salidas:

6 
15 

ver su funcionamiento en línea: ideone

En cuanto a los demás tipos se preguntó acerca de:

  • No trabajar en una matriz, ya que no tiene un método Add.
  • List<T> tiene un método Add pero solo tiene un parámetro.
+1

Ok ...pero * List * ** does ** tiene un método de agregar e intentar eso produce el mismo error. – McGarnagle

+0

Consulte la actualización para obtener una explicación. –

+1

estado pensando en esto para siempre, gracias –

3

Su problema proviene del hecho de que es una matriz, no una colección.

var kvps = new KeyValuePair<string,int>[] { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

debería ser:

var kvps = new KeyValuePair<string, int>[] { 
    new KeyValuePair<string, int>("key1", 1), 
    new KeyValuePair<string, int>("key2", 2) 
}; 

El obsequio es de los corchetes. [] es una matriz. {} es una colección.

+4

Tal vez mi pregunta no fue expresada claramente, pero por favor léala más detenidamente. Mencioné tu ejemplo en mi respuesta. Sé cómo escribir la inicialización, pero me pregunto acerca de la razón subyacente por la que la sintaxis más simple no es posible. – McGarnagle

11

Funciona con el diccionario, porque tiene una sobrecarga para Add que tiene dos argumentos. Las matrices ni siquiera tienen un método Add, y mucho menos uno con dos argumentos.

La clase Dictionary está especialmente diseñado para trabajar con KeyValuePair<,> internamente, que es la única razón por la que no es necesario la llamada al constructor de forma manual, en lugar del de dos argumentos Add se llama y construye la KeyValuePair bajo el capó.

Cada otro IEnumerable<KeyValuePair<,>> no tiene esta aplicación especial y por lo tanto tiene que ser inicializado esta manera:

var array = new KeyValuePair<int, int>[] { 
    new KeyValuePair<int, int>(1, 2), 
    new KeyValuePair<int, int>(3, 4) 
}; 

puede crear el mismo comportamiento con sus propias clases, como digamos que decide implementar esta forma:

class ThreeTupleList<T1, T2, T3> : List<Tuple<T1, T2, T3>> 
{ 
    public void Add(T1 a, T2 b, T3 c) 
    { 
     this.Add(new Tuple<T1, T2, T3>(a, b, c)); 
    } 

    // You can even implement a two-argument Add and mix initializers 
    public void Add(T1 a, T2 b) 
    { 
     this.Add(new Tuple<T1, T2, T3>(a, b, default(T3))); 
    } 
} 

puede inicializar como este, e incluso mezclar tres, dos y un argumento inicializadores:

var mylist = new ThreeTupleList<int, string, double>() 
{ 
    { 1, "foo", 2.3 }, 
    { 4, "bar", 5.6 }, 
    { 7, "no double here" }, 
    null 
}; 
0

Es posible que no lo vea pero tiene la misma sintaxis. La única diferencia es que una matriz toma elementos como una entrada donde un diccionario toma una matriz bidimensional.

int[] a = new int[]{5,6}; 
int[,] a = new int[]{{5,6},{3,6}}; 
Dictionary<int,int> a = new Dictionary<int,int>{{5,6},{3,6}}; 
+3

No es exactamente la razón por la cual este azúcar sintáctico es posible. En realidad, es debido a la implementación del diccionario genérico, que proporciona un '' Add'' de dos argumentos que construye el '' KeyValuePair'' para usted bajo el capó. –

2

Gracias a múltiples que responden por señalar que el método Añadir es la cosa mágica salsa secreta que hace que el trabajo de la sintaxis de inicialización. Así que pude alcanzar mi meta heredando la clase en cuestión (KeyValuePair):

public class InitializableKVPs<T1,T2> : IEnumerable<KeyValuePair<T1,T2>> 
{ 
    public void Add(T1 key, T2 value) 
    { 
     throw new NotImplementedException(); 
    } 

    public IEnumerator<KeyValuePair<string,string>> GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     throw new NotImplementedException(); 
    } 
} 

Esto ahora es aceptado por el compilador:

var kvps = new InitializableKVPs<string,int> { 
    { "key1", 1 }, 
    { "key2", 2 } 
}; 

Editar: La respuesta de Felipe Daubmeier tiene una real y concisa implementación de esto.

+2

Exactamente. Vea mi respuesta actualizada para una implementación muy simple de esto, si solo hereda de un '' ICollection <> '' ya implementado, p. '' List <> '' –

+0

Acabo de enterar que incluso puedes mezclar elementos del inicializador con diferentes números de argumentos :) Actualicé mi respuesta una vez más ... –

1

Anulación IDictionary para la sintaxis de C# 6.0

En caso de que alguien viene aquí, como yo, que buscan ahorrar algunos golpes de teclado para el nuevo diccionario de la sintaxis de inicializador de C# 6.0, se puede hacer, pero requiere que deriva de IDictionary vez . Solo necesita implementar el método this [] set para que esto funcione, lo que deja una tonelada de métodos no implementados.

La implementación de la clase es el siguiente:

// Seriously! Don't do this in production code! Ever!!! 
public class CrazyAdd2 : IDictionary<string, int> 
{ 
    public int this[string key] 
    { 
     get { throw new NotImplementedException(); } 
     set {Console.WriteLine($"([{key}]={value})"); } 
    } 

#region NotImplemented 
// lots of empty methods go here 
#endregion 
} 

luego utilizarlo:

var crazyAdd2 = new CrazyAdd2 
{ 
    ["one"] = 1, 
    ["two"] = 2, 
}; 

y la salida:

([one]=1) 
([two]=2) 

Y aquí está un violín que demuestra todo el asunto:

https://dotnetfiddle.net/Lovy7m

Cuestiones relacionadas