c# Lista <T> o IList <T>



9 Answers

La respuesta menos popular es que a los programadores les gusta fingir que su software será reutilizado en todo el mundo, cuando la mayoría de los proyectos serán mantenidos por una pequeña cantidad de personas y, por muy agradables que sean las interferencias en la interfaz, lo está engañando. tú mismo.

Astronautas de la arquitectura . Las posibilidades de que alguna vez escriba su propio IList que agregue algo a las que ya están en el marco de .NET son tan remotas que es una jalea teórica reservada para las "mejores prácticas".

Obviamente, si se te pregunta qué utilizas en una entrevista, dices IList, sonríe, y ambos se ven complacidos por ser tan inteligentes. O para una API pública, IList. Espero que me entiendas.

c# list generics

¿Puede alguien explicarme por qué querría usar IList sobre Lista en C #?

Pregunta relacionada: ¿Por qué se considera malo exponer la List<T>




Algunas personas dicen "siempre use IList<T> lugar de List<T> ".
Quieren que cambies las firmas de tu método de void Foo(List<T> input) a void Foo(IList<T> input) .

Estas personas están equivocadas.

Es más matizado que eso. Si está devolviendo un IList<T> como parte de la interfaz pública a su biblioteca, deje opciones interesantes para tal vez hacer una lista personalizada en el futuro. Puede que nunca necesites esa opción, pero es un argumento. Creo que es todo el argumento para devolver la interfaz en lugar del tipo concreto. Vale la pena mencionarlo, pero en este caso tiene un defecto grave.

Como contraargumento menor, puede encontrar que cada persona que llama necesita una List<T> todos modos, y el código de llamada está lleno de .ToList()

Pero mucho más importante, si está aceptando un IList como parámetro, debería tener cuidado, ya que IList<T> y List<T> no se comportan de la misma manera. A pesar de la similitud en el nombre, y a pesar de compartir una interfaz , no exponen el mismo contrato .

Supongamos que tiene este método:

public Foo(List<int> a)
{
    a.Add(someNumber);
}

Un colega útil "refactoriza" el método para aceptar IList<int> .

Su código ahora está roto, porque int[] implementa IList<int> , pero tiene un tamaño fijo. El contrato para ICollection<T> (la base de IList<T> ) requiere el código que lo utiliza para verificar el indicador IsReadOnly antes de intentar agregar o eliminar elementos de la colección. El contrato para la List<T> no lo hace.

El principio de sustitución de Liskov (simplificado) establece que un tipo derivado debe poder usarse en lugar de un tipo base, sin condiciones previas o condiciones posteriores adicionales.

Esto se siente como si rompiera el principio de sustitución de Liskov.

 int[] array = new[] {1, 2, 3};
 IList<int> ilist = array;

 ilist.Add(4); // throws System.NotSupportedException
 ilist.Insert(0, 0); // throws System.NotSupportedException
 ilist.Remove(3); // throws System.NotSupportedException
 ilist.RemoveAt(0); // throws System.NotSupportedException

Pero no es así. La respuesta a esto es que el ejemplo usó IList <T> / ICollection <T> incorrecto. Si utiliza una ICollection <T>, debe marcar el indicador IsReadOnly.

if (!ilist.IsReadOnly)
{
   ilist.Add(4);
   ilist.Insert(0, 0); 
   ilist.Remove(3);
   ilist.RemoveAt(0);
}
else
{
   // what were you planning to do if you were given a read only list anyway?
}

Si alguien le pasa una matriz o una lista, su código funcionará bien si marca la bandera cada vez y tiene un respaldo ... Pero realmente; quien hace eso ¿No sabe de antemano si su método necesita una lista que puede incluir miembros adicionales? ¿No especificas eso en la firma del método? ¿Qué iba a hacer exactamente si se le pasara una lista de solo lectura como int[] ?

Puede sustituir una List<T> en el código que usa IList<T> / ICollection<T> correctamente . No puede garantizar que puede sustituir un código IList<T> / ICollection<T> en el uso de la List<T> .

Existe una apelación al Principio de Responsabilidad Única / Principio de Segregación de Interfaz en muchos de los argumentos para usar abstracciones en lugar de tipos concretos. Depende de la interfaz más estrecha posible. En la mayoría de los casos, si está utilizando una List<T> y cree que podría usar una interfaz más estrecha, ¿por qué no IEnumerable<T> ? A menudo esto es mejor si no necesita agregar elementos. Si necesita agregar a la colección, use el tipo concreto, List<T> .

Para mí, IList<T> (y ICollection<T> ) es la peor parte del marco .NET. IsReadOnly viola el principio de menos sorpresa. Una clase, como Array , que nunca permite agregar, insertar o eliminar elementos no debe implementar una interfaz con los métodos Agregar, Insertar y Eliminar. (vea también https://softwareengineering.stackexchange.com/questions/306105/implementing-an-interface-when-you-dont-need-one-of-the-properties )

¿Es IList<T> una buena opción para su organización? Si un colega le pide que cambie la firma de un método para usar IList<T> lugar de List<T> , pregúnteles cómo agregarían un elemento a un IList<T> . Si no conocen IsReadOnly (y la mayoría de las personas no lo saben), entonces no use IList<T> . Siempre.

Tenga en cuenta que el indicador IsReadOnly proviene de ICollection <T> e indica si se pueden agregar o eliminar elementos de la colección; pero solo para realmente confundir las cosas, no indica si se pueden reemplazar, lo que en el caso de Arrays (que devuelve IsReadOnlys == true) puede ser.

Para obtener más información sobre IsReadOnly, consulte la definición de msdn de ICollection <T> .IsReadOnly




Volvería la pregunta un poco, en lugar de justificar por qué debería usar la interfaz en lugar de la implementación concreta, trate de justificar por qué usaría la implementación concreta en lugar de la interfaz. Si no puedes justificarlo, usa la interfaz.




Un principio de TDD y OOP generalmente es programar a una interfaz, no a una implementación.

En este caso específico, ya que básicamente se trata de una construcción de lenguaje, no una personalizada, generalmente no importará, pero digamos, por ejemplo, que List no admitió algo que necesitaba. Si hubiera usado IList en el resto de la aplicación, podría extender la Lista con su propia clase personalizada y aún así podría pasar eso sin refactorizar.

El costo para hacer esto es mínimo, ¿por qué no ahorrarse el dolor de cabeza más adelante? Es de lo que trata el principio de la interfaz.




El caso más importante para usar interfaces sobre implementaciones está en los parámetros de su API. Si su API toma un parámetro de Lista, entonces cualquiera que lo use debe usar Lista. Si el tipo de parámetro es IList, entonces la persona que llama tiene mucha más libertad y puede usar clases de las que nunca escuchó, que pueden no haber existido cuando se escribió su código.




¿Qué pasa si .NET 5.0 reemplaza System.Collections.Generic.List<T> por System.Collection.Generics.LinearList<T> . .NET siempre posee el nombre List<T> pero garantiza que IList<T> es un contrato. Entonces, en mi humilde opinión (al menos yo) no debemos usar el nombre de alguien (aunque en este caso es .NET) y luego nos metemos en problemas.

En el caso de utilizar IList<T> , la persona que llama siempre está preparada para funcionar, y el implementador es libre de cambiar la colección subyacente a cualquier implementación concreta alternativa de IList




Lo haría porque definir un IList o un ICollection se abriría para otras implementaciones de sus interfaces.

Es posible que desee tener un IOrderRepository que define una colección de órdenes en una lista IList o ICollection. Luego podría tener diferentes tipos de implementaciones para proporcionar una lista de órdenes siempre que se ajusten a las "reglas" definidas por su IList o ICollection.




IList <> es casi siempre preferible según el consejo del otro póster, sin embargo, tenga en cuenta que hay un error en .NET 3.5 sp 1 cuando se ejecuta un IList <> a través de más de un ciclo de serialización / deserialización con WCF DataContractSerializer.

Ahora hay un SP para solucionar este error: KB 971030




Normalmente, un buen enfoque es usar IList en su API pública (cuando sea apropiado, y se necesita una lista de semánticas), y luego Listar internamente para implementar la API. Esto le permite cambiar a una implementación diferente de IList sin romper el código que usa su clase.

La lista de nombres de clase puede cambiarse en el siguiente marco .net, pero la interfaz nunca cambiará ya que la interfaz es un contrato.

Tenga en cuenta que, si su API solo se va a utilizar en bucles foreach, etc., es posible que desee considerar simplemente exponer IEnumerable en su lugar.




Related