2009-09-21 17 views
10

Ok, espero que la comunidad en general nos ayude a resolver un debate en el lugar de trabajo que ha estado en curso por un tiempo. Esto tiene que ver con la definición de interfaces que aceptan o devuelven listas de algún tipo. Hay varias maneras de hacer esto:¿Cuál prefiere para las interfaces: T [], IEnumerable <T>, IList <T>, u otra?

public interface Foo 
{ 
    Bar[] Bars { get; } 
    IEnumerable<Bar> Bars { get; } 
    ICollection<Bar> Bars { get; } 
    IList<Bar> Bars { get; } 
} 

Mi preferencia es utilizar IEnumerable para los argumentos y las matrices de valores de retorno:

public interface Foo 
{ 
    void Do(IEnumerable<Bar> bars); 
    Bar[] Bars { get; } 
} 

Mi argumento a favor de este enfoque es que la clase de implementación puede crear una Haga una lista directamente desde IEnumerable y simplemente regrésela con List.ToArray(). Sin embargo, algunos creen que se debe devolver IList en lugar de una matriz. El problema que tengo aquí es que ahora es necesario volver a copiarlo con ReadOnlyCollection antes de volver. La opción de devolver IEnumerable parece problemática para el código del cliente?

¿Qué usas/prefieres? (especialmente con respecto a las bibliotecas que serán utilizadas por otros desarrolladores fuera de su organización)

+1

He visto los errores de mis formas. Como muchos afirman a continuación, es obvio que los desarrolladores no ven las propiedades T [] como Inmutable. Creo que mi abuso de ellos me ha enseñado algunos malos hábitos. Realmente nunca me importó si modificaron el conjunto, ya que era una copia, sin embargo, me he dado cuenta de la confusión que produce. IEnumerable es un manual ganador, creo. –

Respuesta

18

Mi preferencia es IEnumerable<T>. Cualquier otra de las interfaces sugeridas da la apariencia de permitir al consumidor modificar la colección subyacente. Es casi seguro que esto no es lo que quiere hacer, ya que permite a los consumidores modificar silenciosamente una colección interna.

Otro bueno en mi humilde opinión, es ReadOnlyCollection<T>. Permite todas las propiedades divertidas de .Count e Indexer y le dice inequívocamente al consumidor "no puede modificar mis datos".

+3

+1 en cualquier aplicación en la que he trabajado, al menos, 'IEnumerable' es todo lo que necesita el 98% del tiempo. –

+0

No dice que está equivocado, pero no asume que no está resintonizando un lista de solo lectura o una copia superficial (o de alguna otra manera inmutable)? – annakata

+1

@annakata, pero ¿cómo expresar esto a sus consumidores? No puede simplemente mirar el retorno de say IList y saber: 1) no debería modificar esto, 2) es la modificación deshabilitada lanzando (Dictionary :: Values) o 3) devolviéndole una copia completa. Simplemente no es obvio por el uso. Al devolver IEnumerable , sin ambigüedades, se dice "no está tocando mis datos". – JaredPar

15

no regreso matrices - que en realidad son un tipo de retorno terrible de utilizar al crear una API - si realmente necesita una secuencia mutable utilizar la interfaz IList<T> o ICollection<T> o devolver un concreto Collection<T> lugar.

también que sugeriría que lea Arrays considered somewhat harmful por Eric Lippert:

que tiene una cuestión moral de un autor del lenguaje de programación de los libros de texto por otro día de la petición mis opiniones sobre si o no Principiante programadores debería enseñarse cómo usar arrays.

en lugar de realmente Respondo pregunta, le di una larga lista de mis opiniones sobre matrices, cómo uso matrices, arrays de cómo esperamos que sean utilizado en el futuro, y así sucesivamente. Este se pone un poco largo, pero al igual que Pascal, I no tuve tiempo de acortarlo.

Empezaré diciendo cuando definitivamente no debe utilizar matrices, y luego cera más filosófica acerca de la futuro de la programación moderna y la papel de la matriz en el mundo venidero.

+1

No había leído ese enlace lippert antes - +1 solo por eso – annakata

+0

Ídem - buena lectura. –

+2

Gracias por el saludo. :) –

8

Para las colecciones de propiedad que están indexados (y los índices tienen significado semántico necesario), se debe utilizar ReadOnlyCollection<T> (sólo lectura) o IList<T> (lectura/escritura). Es el más flexible y expresivo. Para colecciones no indexadas, use IEnumerable<T> (solo lectura) o ICollection<T> (lectura/escritura).

parámetros del método deben utilizar IEnumerable<T> a menos que 1) tienen que añadir/quitar elementos a la colección (ICollection<T>) o 2) requieren índices para fines semánticas NECESARIO (IList<T>). Si el método puede beneficiarse de la disponibilidad de indexación (como una rutina de clasificación), siempre puede usar as IList<T> o .ToList() cuando eso falla en la implementación.

+0

+1 para usar lo que es apropiado para el escenario ... realmente no es 1 para gobernar, luego todo ... otra forma de decir ReadOnlyCollection vs. IEnumerable es precargado contra la carga diferida. – eglasius

1

Preferiría IEnumerable ya que es el más alto nivel de las interfaces, dando al usuario final la oportunidad de volver a emitir como lo desee. Aunque esto puede proporcionar al usuario una funcionalidad mínima para empezar (básicamente solo enumeración), aún sería suficiente para cubrir prácticamente cualquier necesidad, especialmente con los métodos de extensión, ToArray(), ToList() etc.

1

Creo sobre esto en términos de escribir el código más útil posible: código que puede hacer más.

En esos términos, significa que me gusta aceptar la interfaz más débil posible como argumentos de método, porque eso hace que mi código sea útil en más lugares. En este caso, eso es un IEnumerable<T>. ¿Tienes una matriz? Puedes llamar a mi método. ¿Tienes una lista? Puedes llamar a mi método. ¿Tiene un bloque iterador? Entiendes la idea.

También significa que me gustan mis métodos para devolver la interfaz más fuerte que es conveniente, por lo que el código que se basa en el método puede hacer más fácilmente. En este caso, eso sería IList<T>. Tenga en cuenta que esto no significa que voy a construir una lista solo para que pueda devolverla. Simplemente significa que si yo ya tengo que implementa IList<T>, también puedo usarlo.

Tenga en cuenta que soy un poco poco convencional con respecto a los tipos de devolución. Un enfoque más típico es también devolver los tipos más débiles de los métodos para evitar encerrarse en una implementación específica.

+1

... si ya tengo alguno que implementa IList , también puedo usarlo ... Sí, veo tu punto; sin embargo, esto puede causar efectos secundarios extraños. Al menos debe envolverlo en un ReadOnlyCollection (). –

+1

@csharptest.net si usted argumenta que un IList se debe empaquetar en la Colección ReadOnly, ¿por qué devolvería un Array usted mismo, que está lejos de ser de solo lectura después de que cualquier elemento puede ser sustituido por cualquier elemento asignable al tipo –

+0

? Usted está en lo correcto aquí; mi creencia equivocada era que sería obvio que obtendrías una copia y que no tendría sentido modificarla. Admito que estaba claramente equivocado en esta discusión ... Nunca se me ocurrió que la gente haría "Foo.Bars [0] = x" o alguna tontería semejante. De todos modos, gracias por ayudarme a aclararme +1 :) –

0

IEnumerable < T> es muy útil para la iteración de evaluación diferida, especialmente en escenarios que usan el método de encadenamiento.

Pero como un tipo de devolución para un nivel de acceso de datos típico, una propiedad Count es a menudo útil, y preferiría devolver un ICollection < T> con una propiedad Count o posiblemente IList < T> si creo que los consumidores típicos quiero usar un indexador

Esto también es una indicación para la persona que llama que la colección se ha materializado realmente. Y así la persona que llama puede recorrer la colección devuelta sin obtener excepciones del nivel de acceso a datos. Esto puede ser importante Por ejemplo, un servicio puede generar una secuencia (por ejemplo, SOAP) de la colección devuelta. Puede ser incómodo si se lanza una excepción desde la capa de acceso a datos mientras se genera la secuencia debido a la iteración de evaluación diferida, ya que la secuencia de salida ya está parcialmente escrita cuando se lanza la excepción.

0

Puesto que los métodos de extensión LINQ se añadieron a IEnumerable <T>, me he dado cuenta que mi uso de las otras interfaces ha disminuido considerablemente; probablemente alrededor del 80%. Yo solía usar List <T> religiosamente ya que tenía métodos que aceptaban delegados para evaluaciones perezosas como Find, FindAll, For Each y similares. Dado que está disponible a través de las extensiones de System.Linq, he reemplazado todas las referencias con IEnumerable <T> referencias.

0

No iría con la matriz, es un tipo que permite la modificación pero no tiene agregar/eliminar ... algo así como lo peor del paquete.Si quiero permitir modificaciones, entonces usaría un tipo que admita agregar/eliminar.

Cuando desee evitar modificaciones, ya las está envolviendo/copiando, por lo que no veo qué le pasa a un IEnumerable o a ReadOnlyCollection. Me gustaría ir con el último ... algo que no me gusta de IEnumerable es que es vago por naturaleza, sin embargo, cuando estás usando datos precargados solo para envolverlo, el código de llamada que funciona con él tiende a suponer que datos cargados o tienen líneas "innecesarias" adicionales :(... que pueden obtener resultados desagradables durante el cambio

Cuestiones relacionadas