ejemplo c#

ejemplo - list object c#



¿Por qué usar IList o List? (10)

Aquí está mi respuesta en este mundo .NET 4.5+ .

Use IList <T> y IReadonlyList <T> ,
en lugar de List <T> , porque ReadonlyList <T> no existe.

IList <T> parece tan consistente con IReadonlyList <T>

  • Use IEnumerable <T> para exposición mínima (propiedad) o requisito (parámetro) si foreach es la única forma de usarlo.
  • Use IReadonlyList <T> si también necesita exponer / usar Count y [] indexer.
  • Use IList <T> si también permite que las personas que llaman agreguen / actualicen / eliminen elementos

porque List <T> implementa IReadonlyList <T> , no necesita ninguna conversión explícita.

Una clase de ejemplo:

// manipulate the list within the class private List<int> _numbers; // callers can add/update/remove elements, but cannot reassign a new list to this property public IList<int> Numbers { get { return _numbers; } } // callers can use: .Count and .ReadonlyNumbers[idx], but cannot add/update/remove elements public IReadOnlyList<int> ReadonlyNumbers { get { return _numbers; } }

Sé que ha habido muchas publicaciones sobre esto, pero aún me confunde por qué debería pasar en una interfaz como IList y devolver una interfaz como IList en lugar de la lista concreta.

Leí muchas publicaciones que decían cómo esto facilita la modificación de la implementación más adelante, pero simplemente no veo cómo funciona.

Di si tengo este método

public class SomeClass { public bool IsChecked { get; set; } } public void LogAllChecked(IList<SomeClass> someClasses) { foreach (var s in someClasses) { if (s.IsChecked) { // log } } }

No estoy seguro de cómo en el futuro usar IList me ayudará.

¿Qué tal si ya estoy en el método? ¿Debo seguir usando IList?

public void LogAllChecked(IList<SomeClass> someClasses) { //why not List<string> myStrings = new List<string>() IList<string> myStrings = new List<string>(); foreach (var s in someClasses) { if (s.IsChecked) { myStrings.Add(s.IsChecked.ToString()); } } }

¿Qué obtengo por usar IList ahora?

public IList<int> onlySomeInts(IList<int> myInts) { IList<int> store = new List<int>(); foreach (var i in myInts) { if (i % 2 == 0) { store.Add(i); } } return store; }

¿que tal ahora? ¿Hay alguna implementación nueva de una lista de int que tendré que cambiar?

Así que, básicamente, necesito ver algunos ejemplos de código real de cómo si yo estuviera usando IList habría resuelto algún problema antes de simplemente tomar la Lista en todo.

Según mis lecturas, creo que podría haber usado IEnumberable en vez de IList ya que solo estoy revisando cosas.

Editar Entonces he estado jugando con algunos de mis métodos sobre cómo hacer esto. Todavía no estoy seguro sobre el tipo de devolución (si debería hacerlo más concreto o una interfaz)

public class CardFrmVm { public IList<TravelFeaturesVm> TravelFeaturesVm { get; set; } public IList<WarrantyFeaturesVm> WarrantyFeaturesVm { get; set; } public CardFrmVm() { WarrantyFeaturesVm = new List<WarrantyFeaturesVm>(); TravelFeaturesVm = new List<TravelFeaturesVm>(); } } public class WarrantyFeaturesVm : AvailableFeatureVm { } public class TravelFeaturesVm : AvailableFeatureVm { } public class AvailableFeatureVm { public Guid FeatureId { get; set; } public bool HasFeature { get; set; } public string Name { get; set; } } private IList<AvailableFeature> FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm) { List<AvailableFeature> availableFeatures = new List<AvailableFeature>(); foreach (var f in avaliableFeaturesVm) { if (f.HasFeature) { // nhibernate call to Load<>() AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId); availableFeatures.Add(availableFeature); } } return availableFeatures; }

Así que ahora estoy devolviendo IList por el simple hecho de que luego agregaré esto a mi modelo de dominio que tiene una propiedad como esta

public virtual IList<AvailableFeature> AvailableFeatures { get; set; }

lo anterior es un IList en sí mismo, ya que es lo que parece ser el estándar para usar con nhibernate. De lo contrario, podría haber devuelto IEnumberable pero no estoy seguro. Todavía no se puede saber qué es lo que el usuario necesitaría al 100% (ahí es donde devolver un concreto tiene una ventaja).

Editar 2

¿También estaba pensando qué pasaría si quisiera pasar por referencia en mi método?

private void FillAvailableFeatures(IEnumerable<AvailableFeatureVm> avaliableFeaturesVm, IList<AvailableFeature> toFill) { foreach (var f in avaliableFeaturesVm) { if (f.HasFeature) { // nhibernate call to Load<>() AvailableFeature availableFeature = featureService.LoadAvaliableFeatureById(f.FeatureId); toFill.Add(availableFeature); } } }

¿Me encontraría con problemas con esto? ¿Desde cuándo no podrían pasar en una matriz (que tiene un tamaño fijo)? ¿Sería mejor tal vez para una lista concreta?


Aquí hay tres preguntas: ¿qué tipo debo usar para un parámetro formal? ¿Qué debería usar para una variable local? y ¿qué debería usar para un tipo de devolución?

Parámetros formales:

El principio aquí es no pedir más de lo que necesita . IEnumerable<T> comunica "Necesito obtener los elementos de esta secuencia de principio a fin". IList<T> comunica "Necesito obtener y establecer los elementos de esta secuencia en orden arbitrario". List<T> comunica "Necesito obtener y configurar los elementos de esta secuencia en orden arbitrario y solo acepto listas; no acepto matrices".

Al pedir más de lo que necesita, (1) hace que la persona que llama haga un trabajo innecesario para satisfacer sus demandas innecesarias, y (2) comunica falsedades al lector. Pregunte solo por lo que va a usar. De esta forma, si la persona que llama tiene una secuencia, no necesitan llamar a ToList para satisfacer su demanda.

Variables locales:

Usa lo que quieras Es tu método. Usted es el único que puede ver los detalles de implementación interna del método.

Tipo de devolución:

Mismo principio que antes, invertido. Ofrezca el mínimo que su interlocutor necesita. Si la persona que llama solo requiere la capacidad de enumerar la secuencia, solo déles un IEnumerable<T> .


Aquí hay un ejemplo: tuve un proyecto en el que nuestras listas se volvieron muy grandes y la fragmentación resultante del gran montón de objetos perjudicaba el rendimiento. Reemplazamos la Lista con LinkedList. LinkedList no contiene una matriz, por lo que, de repente, casi no tuvimos uso del gran montón de objetos.

Sobre todo, utilizamos las listas como IEnumerable<T> , de todos modos, por lo que no hubo más cambios necesarios. (Y sí, recomendaría declarar referencias como IEnumerable si todo lo que hace es enumerarlas). En un par de lugares, necesitábamos el indexador de listas, por lo que escribimos un ineficaz envoltorio IList<T> alrededor de las listas vinculadas. Necesitábamos el indexador de listas con poca frecuencia, por lo que la ineficiencia no era un problema. Si lo hubiera sido, podríamos haber proporcionado alguna otra implementación de IList, tal vez como una colección de arreglos suficientemente pequeños, que hubieran sido más eficientemente indexables y al mismo tiempo evitaran objetos grandes.

Al final, es posible que deba reemplazar una implementación por cualquier motivo; el rendimiento es solo una posibilidad. Independientemente de la razón, usar el tipo menos derivado posible reducirá la necesidad de cambios en su código cuando cambie el tipo específico de tiempo de ejecución de sus objetos.


Dentro del método, debe usar var , en lugar de IList o List . Cuando su origen de datos cambie para proceder de un método, su método onlySomeInts sobrevivirá.

La razón para usar IList lugar de List como parámetros, es porque muchas cosas implementan IList (List y [], como dos ejemplos), pero solo una cosa implementa List . Es más flexible codificar en la interfaz.

Si solo enumera los valores, debe usar IEnumerable . Cada tipo de tipo de datos que puede contener más de un valor implementa IEnumerable (o debería) y hace que su método sea enormemente flexible.


La razón más práctica que he visto fue dada por Jeffrey Richter en CLR a través de C #.

El patrón es tomar la clase o interfaz más baja posible para sus argumentos y devolver la clase o interfaz más específica posible para sus tipos de devolución. Esto les brinda a sus interlocutores la mayor flexibilidad para pasar los tipos a sus métodos y la mayor cantidad de oportunidades para emitir / reutilizar los valores devueltos.

Por ejemplo, el siguiente método

public void PrintTypes(IEnumerable items) { foreach(var item in items) Console.WriteLine(item.GetType().FullName); }

permite que el método se denomine pasando en cualquier tipo que se pueda convertir en enumerable . Si fueras más específico

public void PrintTypes(List items)

luego, digamos, si tenía una matriz y deseaba imprimir sus nombres de tipo en la consola, primero tendría que crear una nueva lista y completarla con sus tipos. Y, si utilizó una implementación genérica, solo podrá usar un método que funcione para cualquier objeto solo con objetos de un tipo específico.

Cuando se habla de tipos de devolución, cuanto más específico sea, más llamadores podrán estar con él.

public List<string> GetNames()

puede usar este tipo de devolución para repetir los nombres

foreach(var name in GetNames())

o puede indexar directamente en la colección

Console.WriteLine(GetNames()[0])

Mientras que, si estuvieras recibiendo un tipo menos específico

public IEnumerable GetNames()

Tendría que masajear el tipo de devolución para obtener el primer valor

Console.WriteLine(GetNames().OfType<string>().First());


Respuesta corta:

Usted pasa la interfaz para que no importa qué implementación concreta de esa interfaz use, su código lo respaldará.

Si utiliza una implementación concreta de la lista, el código no admitirá otra implementación de la misma lista.

Lea un poco sobre herencia y polimorfismo .


Siempre es una buena idea reducir las dependencias entre su código tanto como sea posible.

Teniendo esto en cuenta, tiene más sentido pasar tipos con el menor número posible de dependencias externas y devolver el mismo. Sin embargo, esto podría ser diferente según la visibilidad de sus métodos y sus firmas.

Si sus métodos forman parte de una interfaz, los métodos deberán definirse utilizando los tipos disponibles para esa interfaz. Es probable que los tipos de concreto no estén disponibles para las interfaces, por lo que tendrían que devolver tipos no concretos. Te gustaría hacer esto si estuvieras creando un marco, por ejemplo.

Sin embargo, si no está escribiendo un marco, puede ser ventajoso pasar el parámetro con los tipos más débiles posibles (es decir, clases base, interfaces o incluso delegados) y devolver tipos concretos. Eso le da a la persona que llama la capacidad de hacer tanto como sea posible con el objeto devuelto, incluso si se emite como una interfaz. Sin embargo, esto hace que el método sea más frágil, ya que cualquier cambio en el tipo de objeto devuelto puede romper el código de llamada. En la práctica, sin embargo, eso generalmente no es un problema importante.


Usar IList en lugar de List hace que las pruebas de unidades de escritura sean mucho más fáciles. Le permite usar una biblioteca ''Mocking'' para pasar y devolver datos.

La otra razón general para usar interfaces es exponer la cantidad mínima de conocimiento necesario para el usuario de un objeto.

Considere el caso (artificial) donde tengo un objeto de datos que implementa IList.

public class MyDataObject : IList<int> { public void Method1() { ... } // etc }

Sus funciones anteriores solo se preocupan por poder iterar sobre una lista. Idealmente, no deberían necesitar saber quién implementa esa lista o cómo la implementan.

En su ejemplo, IEnumerable es una mejor opción que usted pensó.


Usted acepta una interfaz como parámetro para un método porque eso le permite a la persona que llama enviar diferentes tipos concretos como argumentos. Dado su método de ejemplo LogAllChecked, el parámetro someClasses podría ser de varios tipos, y para la persona que escribe el método, todos podrían ser equivalentes (es decir, escribiría exactamente el mismo código independientemente del tipo del parámetro). Pero para la persona que llama al método, puede hacer una gran diferencia: si tienen un conjunto y usted está pidiendo una lista, tienen que cambiar el conjunto a una lista o vv cada vez que llaman al método, una pérdida total de tiempo de un programador y POV de rendimiento.

Si devuelve una interfaz o un tipo concreto depende de lo que quiera que hagan las personas que llaman con el objeto que ha creado: esta es una decisión de diseño de la API, y no hay una regla rígida. Debes sopesar su capacidad para hacer un uso completo del objeto en contra de su capacidad para usar fácilmente una parte de la funcionalidad de los objetos (y, por supuesto, si QUIERES que hagan un uso completo del objeto). Por ejemplo, si devuelve un IEnumerable, entonces los está limitando a la iteración: no pueden agregar o eliminar elementos de su objeto, solo pueden actuar contra los objetos. Si necesita exponer una colección fuera de una clase, pero no quiere que la persona que llama cambie la colección, esta es una forma de hacerlo. Por otro lado, si devuelve una colección vacía que espera / desea que rellene, entonces un IEnumerable no es adecuado.


IEnumerable<T> permite iterar a través de una colección. ICollection<T> basa en esto y también permite agregar y eliminar elementos. IList<T> también permite acceder y modificarlos en un índice específico. Al exponer el que usted espera que su consumidor trabaje, usted es libre de cambiar su implementación. List<T> pasa a implementar las tres de esas interfaces.

Si expone su propiedad como una List<T> o incluso una IList<T> cuando todo lo que quiere que su consumidor tenga es la capacidad de recorrer la colección. Entonces podrían llegar a depender del hecho de que pueden modificar la lista. Luego, más adelante, si decide convertir el almacén de datos real de una List<T> a un Dictionary<T,U> y expone las claves del diccionario como el valor real de la propiedad (he tenido que hacer exactamente esto antes). Entonces, los consumidores que han llegado a esperar que sus cambios se verán reflejados dentro de su clase ya no tendrán esa capacidad. ¡Ese es un gran problema! Si expone la List<T> como IEnumerable<T> puede predecir cómodamente que su colección no se está modificando externamente. Ese es uno de los poderes de exponer List<T> como cualquiera de las interfaces anteriores.

Este nivel de abstracción va en la otra dirección cuando pertenece a los parámetros del método. Cuando pasa su lista a un método que acepta IEnumerable<T> , puede estar seguro de que su lista no se va a modificar. Cuando eres la persona que implementa el método y dices que aceptas un IEnumerable<T> porque todo lo que tienes que hacer es iterar a través de esa lista. Luego, la persona que llama al método puede llamarlo con cualquier tipo de datos enumerable. Esto permite que su código se use de formas inesperadas, pero perfectamente válidas.

De esto se deduce que la implementación de su método puede representar sus variables locales como lo desee. Los detalles de implementación no están expuestos. Dejándote libre para cambiar tu código a algo mejor sin afectar a las personas que llaman a tu código.

No puedes predecir el futuro. Asumiendo que el tipo de propiedad siempre será beneficioso ya que una List<T> está limitando de inmediato su capacidad de adaptarse a las expectativas imprevistas de su código. Sí, nunca puede cambiar ese tipo de datos de una List<T> pero puede estar seguro de que si es necesario. Tu código está listo para eso.