tipos - ¿Por qué puedo inicializar una lista como una matriz en C#?
matriz 4x4 c# (6)
Hoy me sorprendí al encontrar que en C # puedo hacer:
List<int> a = new List<int> { 1, 2, 3 };
¿Por 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 ...
Es llamado azúcar sintáctico .
List<T>
es la clase "simple", pero el compilador le da un tratamiento especial para facilitarle la vida.
Este es el llamado MSDN . Necesita implementar IEnumerable<T>
y método Add
.
Esto es parte de la sintaxis del inicializador de colecciones en .NET. Puede usar esta sintaxis en cualquier colección que cree, siempre que:
Implementa
IEnumerable
(preferiblementeIEnumerable<T>
)Tiene un método llamado
Add(...)
Lo que sucede es que se llama al constructor predeterminado, y luego 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;
Puede llamar a un constructor alternativo si lo desea, por ejemplo para evitar sobredimensionar la List<T>
durante el crecimiento, 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 elementos:
var grades = new Dictionary<string, int>
{
{ "Suzy", 100 },
{ "David", 98 },
{ "Karen", 73 }
};
Es más o menos idéntico a:
var temp = new Dictionary<string, int>();
temp.Add("Suzy", 100);
temp.Add("David", 98);
temp.Add("Karen", 73);
var grades = temp;
Entonces, para agregar esto a su propia clase, todo lo que necesita hacer, como se mencionó, es implementar IEnumerable
(nuevamente, preferiblemente 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();
}
}
Entonces puede usarlo tal como lo hacen las colecciones de BCL:
public class MyProgram
{
private SomeCollection<int> _myCollection = new SomeCollection<int> { 13, 5, 7 };
// ...
}
(Para obtener más información, vea MSDN )
Funciona gracias a MSDN que básicamente requieren que la colección implemente un método Add y que hará el trabajo por usted.
La matriz como la sintaxis 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) estableciendo una propiedad de solo lectura, 2) estableciendo 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 funcionará perfectamente, aunque 1) MyList es de solo lectura y 2) Establecí una lista con el inicializador de matriz.
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 {}
similar a una serie de llamadas Add()
que son perfectamente legales incluso en un campo de solo lectura.
Otra cosa interesante de los inicializadores de colecciones es que puedes tener múltiples sobrecargas del método Add
y puedes 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.
Según la especificación de la versión 3.0 de C # "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.