2012-01-13 12 views
127

Hoy me sorprendió encontrar que en C# que puedo hacer:¿Por qué puedo inicializar una lista como una matriz en C#?

List<int> a = new List<int> { 1, 2, 3 }; 

qué puedo hacer esto? ¿Qué constructor se llama? ¿Cómo puedo hacer esto con mis propias clases? Sé que esta es la forma de inicializar matrices, pero las matrices son elementos de idioma y las Listas son objetos simples ...

+1

Esta pregunta puede ser de ayuda: http://stackoverflow.com/questions/1744967/in-c-is-there-a-way-to-write-custom-object-initializers-for-new-data- tipos –

+10

Bastante impresionante ¿eh? También puede hacer un código similar para inicializar diccionarios: '{{" key1 "," value1 "}, {" key2 "," value2 "}}' – danludwig

+0

Desde otra perspectiva, esta matriz como la sintaxis de inicialización nos da un recordatorio de que subyace tipo de datos de una 'Lista ' es en realidad una matriz solamente. Odio el equipo C# por el hecho de que no lo llaman 'ArrayList ' que suena tan obvio y natural. – RBT

Respuesta

180

Esto forma parte de la sintaxis del inicializador de recopilación en .NET. Puede utilizar esta sintaxis en cualquier colección se crea siempre que:

  • Implementa IEnumerable (preferiblemente IEnumerable<T>)

  • Tiene un método llamado Add(...)

Lo que pasa es que el se llama al constructor predeterminado y se llama a Add(...) para cada miembro del inicializador.

Por lo tanto, estos dos bloques son más o menos idénticos:

List<int> a = new List<int> { 1, 2, 3 }; 

Y

List<int> temp = new List<int>(); 
temp.Add(1); 
temp.Add(2); 
temp.Add(3); 
List<int> a = temp; 

Usted puede llamada de una constructora alternativo si lo desea, por ejemplo, para evitar el exceso de dimensionar el List<T> durante creciente, etc .:

// Notice, calls the List constructor that takes an int arg 
// for initial capacity, then Add()'s three items. 
List<int> a = new List<int>(3) { 1, 2, 3, } 

Tenga en cuenta que el método Add() no necesita tomar un solo elemento, por ejemplo el método Add() para Dictionary<TKey, TValue> toma dos artículos:

var grades = new Dictionary<string, int> 
    { 
     { "Suzy", 100 }, 
     { "David", 98 }, 
     { "Karen", 73 } 
    }; 

es aproximadamente igual a:

var temp = new Dictionary<string, int>(); 
temp.Add("Suzy", 100); 
temp.Add("David", 98); 
temp.Add("Karen", 73); 
var grades = temp; 

lo tanto, para agregar esto a su propia clase, todo lo que necesita hacer, como se ha mencionado, es aplicar IEnumerable (de nuevo, preferentemente IEnumerable<T>) y crear uno o más métodos: Add()

public class SomeCollection<T> : IEnumerable<T> 
{ 
    // implement Add() methods appropriate for your collection 
    public void Add(T item) 
    { 
     // your add logic  
    } 

    // implement your enumerators for IEnumerable<T> (and IEnumerable) 
    public IEnumerator<T> GetEnumerator() 
    { 
     // your implementation 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

A continuación, puede usarlo al igual que las colecciones de BCL hacen:

public class MyProgram 
{ 
    private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };  

    // ... 
} 

(Para más información, ver la MSDN)

+0

Hah, gracias :-) –

+29

Buena respuesta, aunque observo que no es del todo exacto decir "exactamente idéntico" en su primer ejemplo. Es exactamente idéntico a 'List temp = new List (); temp.Add (1); ... List a = temp; 'Es decir, la variable' a' no se inicializa hasta * after * se llaman todas las adiciones. De lo contrario, sería legal hacer algo como 'List a = new List () {a.Count, a.Count, a.Count};' que es una locura. –

+0

@EricLippert, 'List a = new List () {a.Count, a.Count, a.Count};' no es legal en ninguno de los casos porque no tiene una referencia en ese momento. Creo que su fragmento inicial estaba perfectamente bien. – user606723

11

Se llama así syntactic sugar.

List<T> es la clase "simple", pero el compilador le da un tratamiento especial para hacer su vida más fácil.

Este se llama collection initializer. Debe implementar el método IEnumerable<T> y Add.

6

Funciona gracias a collection initializers que básicamente requieren que la colección implemente un método Add y que hará el trabajo por usted.

8

Según el C# Version 3.0 Specification "El objeto de colección al que se aplica un inicializador de colección debe ser de un tipo que implemente System.Collections.Generic.ICollection para exactamente una T."

Sin embargo, esta información parece ser inexacta a partir de este escrito; ver la aclaración de Eric Lippert en los comentarios a continuación.

+10

Esa página no es precisa. Gracias por traer esto a mi atención. Esa debe ser una documentación previa preliminar que de alguna manera nunca se eliminó. El diseño original requería ICollection, pero ese no es el diseño final en el que nos decidimos. –

+1

Gracias por la aclaración. –

6

Otra cosa interesante acerca de los inicializadores de colecciones es que puede tener múltiples sobrecargas del método Add y ¡puede llamarlas todas en el mismo inicializador! Por ejemplo, esto funciona:

public class MyCollection<T> : IEnumerable<T> 
{ 
    public void Add(T item, int number) 
    { 

    } 
    public void Add(T item, string text) 
    { 

    } 
    public bool Add(T item) //return type could be anything 
    { 

    } 
} 

var myCollection = new MyCollection<bool> 
{ 
    true, 
    { false, 0 }, 
    { true, "" }, 
    false 
}; 

Llama a las sobrecargas correctas. Además, busca solo el método con el nombre Add, el tipo de devolución podría ser cualquier cosa.

0

La sintaxis similar a la matriz se está convirtiendo en una serie de llamadas Add().

Para ver esto en un ejemplo mucho más interesante, considere el siguiente código en el que hago dos cosas interesantes que primero suenan ilegales en C#, 1) establecer una propiedad de solo lectura, 2) establecer una lista con una matriz como inicializador.

public class MyClass 
{ 
    public MyClass() 
    { 
     _list = new List<string>(); 
    } 
    private IList<string> _list; 
    public IList<string> MyList 
    { 
     get 
     { 
      return _list; 
     } 
    } 
} 
//In some other method 
var sample = new MyClass 
{ 
    MyList = {"a", "b"} 
}; 

Este código funciona perfectamente, aunque 1) MyList es de sólo lectura y 2) I establece una lista con matriz inicializador.

La razón por la que esto funciona es porque en el código que forma parte de un intializador de objetos el compilador siempre convierte cualquier sintaxis {} en una serie de llamadas Add() que son perfectamente legales incluso en un campo de solo lectura.

Cuestiones relacionadas