Cuándo usar IList y cuándo usar List

Sé que IList es la interfaz y List es el tipo concreto, pero todavía no sé cuándo usar cada uno. Lo que estoy haciendo ahora es que si no necesito los métodos de Ordenar o Buscar Todos, uso la interfaz. ¿Estoy en lo cierto? ¿Hay una mejor manera de decidir cuándo usar la interfaz o el tipo concreto?

Hay dos reglas que sigo:

  • Acepta el tipo más básico que funcionará
  • Devuelve el tipo más rico que tu usuario necesitará

Por lo tanto, al escribir una función o método que toma una colección, escríbala para no tomar una lista, sino un IList , una ICollection o IEnumerable . Las interfaces genéricas seguirán funcionando incluso para listas heterogéneas porque System.Object también puede ser una T. Hacer esto le ahorrará dolor de cabeza si decide utilizar una stack o alguna otra estructura de datos más adelante en la carretera. Si todo lo que necesita hacer en la función es por ahí, IEnumerable es realmente todo lo que debe pedir.

Por otro lado, cuando se devuelve un objeto de una función, se desea darle al usuario el conjunto de operaciones más completo posible sin que tenga que dar vueltas. Entonces, en ese caso, si es una Lista internamente, devuelva una copia como una Lista .

Las pautas de Microsoft comprobadas por FxCop desalientan el uso de List en las API públicas, prefieren IList .

Por cierto, ahora casi siempre declaro matrices unidimensionales como IList , lo que significa que puedo usar consistentemente la propiedad IList .Count en lugar de Array.Length. Por ejemplo:

 public interface IMyApi { IList GetReadOnlyValues(); } public class MyApiImplementation : IMyApi { public IList GetReadOnlyValues() { List myList = new List(); ... populate list return myList.AsReadOnly(); } } public class MyMockApiImplementationForUnitTests : IMyApi { public IList GetReadOnlyValues() { IList testValues = new int[] { 1, 2, 3 }; return testValues; } } 

Hay algo importante que las personas siempre parecen pasar por alto:

Puede pasar una matriz simple a algo que acepte un parámetro IList , y luego puede llamar a IList.Add() y recibirá una excepción en tiempo de ejecución:

Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.

Por ejemplo, considere el siguiente código:

 private void test(IList list) { list.Add(1); } 

Si llama a eso de la siguiente manera, obtendrá una excepción de tiempo de ejecución:

 int[] array = new int[0]; test(array); 

Esto sucede porque el uso de matrices simples con IList viola el principio de sustitución de Liskov.

Por este motivo, si llama a IList.Add() , puede considerar la posibilidad de solicitar una List lugar de una IList .

Estoy de acuerdo con el consejo de Lee de tomar los parámetros, pero no regresar.

Si especifica sus métodos para devolver una interfaz, significa que puede cambiar la implementación exacta más adelante sin que el método consumidor lo sepa. Pensé que nunca necesitaría cambiar de una Lista pero tuve que cambiar más tarde para usar una biblioteca de listas personalizada para la funcionalidad adicional que proporcionaba. Como solo había devuelto IList , ninguna de las personas que usaban la biblioteca tuvo que cambiar su código.

Por supuesto, solo es necesario aplicarlo a métodos que son visibles externamente (es decir, métodos públicos). Yo personalmente uso interfaces incluso en código interno, pero como usted mismo puede cambiar todo el código si realiza cambios de última hora, no es estrictamente necesario.

IEnumerable debe intentar usar el tipo menos específico que se adapte a su propósito. IEnumerable es menos específico que IList. Utiliza IEnumerable cuando desea recorrer los elementos de una colección

IList IList implementa IEnumerable. Debe usar IList cuando necesite acceder por índice a su colección, agregar y eliminar elementos, etc.

List List implementa IList

Siempre es mejor usar el tipo base más bajo posible. Esto le da al implementador de su interfaz, o consumidor de su método, la oportunidad de usar lo que quiera detrás de escena.

Para las colecciones, debe intentar usar IEnumerable siempre que sea posible. Esto le da la mayor flexibilidad, pero no siempre es adecuado.

Si trabajas con un único método (o incluso en una única clase o ensamblaje en algunos casos) y nadie va a ver lo que estás haciendo, utiliza la plenitud de una lista. Pero si está interactuando con un código externo, como cuando devuelve una lista de un método, solo quiere declarar la interfaz sin vincularse necesariamente con una implementación específica, especialmente si no tiene control sobre quién comstack en su contra. codificar después Si comenzó con un tipo concreto y decidió cambiar a otro, incluso si utiliza la misma interfaz, va a romper el código de otra persona a menos que haya comenzado con una interfaz o tipo de base abstracta.

No creo que haya reglas duras y rápidas para este tipo de cosas, pero por lo general sigo la pauta de usar la forma más liviana posible hasta que sea absolutamente necesario.

Por ejemplo, supongamos que tiene una clase Person y una clase Group . Una instancia de Group tiene muchas personas, por lo que una lista aquí tendría sentido. Cuando declaro que el objeto de la lista en el Group I IList un IList y lo instanciaré como una List .

 public class Group { private IList people; public Group() { this.people = new List(); } } 

Y, si ni siquiera necesita todo en IList , siempre puede usar IEnumerable también. Con comstackdores y procesadores modernos, no creo que haya realmente una diferencia de velocidad, por lo que esto es más una cuestión de estilo.

En general, es mejor utilizar el tipo de uso más general, en este caso IList o incluso mejor la interfaz IEnumerable, para que pueda cambiar la implementación convenientemente más adelante.

Sin embargo, en .NET 2.0, hay algo molesto: IList no tiene un método Sort () . Puede usar un adaptador suministrado en su lugar:

 ArrayList.Adapter(list).Sort() 

El objeto AList le permite crear una lista, agregarle elementos, eliminarla, actualizarla, indexarla, etc. La lista se usa siempre que desee una Lista genérica donde especifique el tipo de objeto y eso es todo.

IList por otro lado es una interfaz. Básicamente, si desea crear su propio tipo de lista, diga una clase de lista llamada Lista de libros, luego puede usar la interfaz para darle los métodos básicos y la estructura de su nueva clase. IList es para cuando quieres crear tu propia subclase especial que implemente Lista.

Otra diferencia es: IList es una interfaz y no se puede crear una instancia. La lista es una clase y se puede crear una instancia. Significa:

 IList MyList = new IList(); List MyList = new List 

En situaciones que generalmente encuentro, rara vez uso IList directamente.

Usualmente lo uso como argumento para un método

 void ProcessArrayData(IList almostAnyTypeOfArray) { // Do some stuff with the IList array } 

Esto me permitirá hacer un procesamiento genérico en casi cualquier matriz en .NET framework, a menos que use IEnumerable y no IList, lo que sucede algunas veces.

Realmente se reduce al tipo de funcionalidad que necesita. Sugeriría usar la clase List en la mayoría de los casos. IList es mejor para cuando necesita crear una matriz personalizada que podría tener algunas reglas muy específicas que le gustaría encapsular dentro de una colección para que no se repita, pero aún desea que .NET la reconozca como una lista.

Debería usar la interfaz solo si la necesita, por ejemplo, si su lista se transfiere a una implementación de IList que no sea List. Esto es cierto cuando, por ejemplo, utiliza NHibernate, que arroja ILists en un objeto de bolsa NHibernate cuando recupera datos.

Si List es la única implementación que alguna vez usará para una determinada colección, no dude en declararla como una implementación concreta de la Lista.