2010-06-28 10 views
9

Siempre he pensado que devolver matrices era mejor que una lista al tener una API pública, pero parece que ahora hay todas estas funciones en listas que están disponibles a través de LINQ, etc.cuál es el mejor tipo de colección para devolver en una API

¿Se ha cambiado aquí la mejor práctica para devolver colecciones de primitivas u objetos?

por ejemplo:

Order[] GetOrders(); 
List<Order> GetOrders(); 
IEnumerable<Order> GetOrders(); 
IQueryable<Order> Get Orders(); 
+0

Tenga en cuenta que todos los tipos anteriores admiten Linq en diversos grados, ya sea a través de 'IEnumerable ' o 'IQueryable '. –

Respuesta

5

Creo que el tipo usado más comúnmente es IEnumerable<T>

  • Con un tipo IEnumerable<T> retorno puede utilizar la palabra clave yield y esto permite la enumeración retrasado y ejecución de su método . (Una queja común, por ejemplo, es que System.IO.Path.GetFiles() NO devuelve un IEnumerable<T> sino que devuelve una matriz, lo que significa que cuando llama al método, todos los elementos deben enumerarse independientemente de si los necesita o no. usted tiene la misma desventaja con List<T>)
  • la mayoría de los métodos de extensión LINQ funcionan en una IEnumerable<T>
  • el IEnumerable<T> tipo de retorno no asumir nada específico acerca de la persona que llama. Si la persona que llama necesita una lista o matriz, siempre puede crear una.
  • IEnumerable<T> está implementado por List<T> y Array y, por lo tanto, es fácil cambiar la implementación de su método y aún respaldar el tipo de devolución anterior.
+0

Solo asegúrate de no permitir que alguien lance tu 'IEnumerable ' a, por ejemplo, una 'List ' y cámbiala si eso rompería una invariante de la clase. Ver mi respuesta para más detalles. –

5

¿Desea que la colección sea inmutable? IEnumerable<T> Mutable? IList<T>

¿Necesitas índices? IList<T>

Con una lista o matriz, el consumidor API tiene plena capacidad para añadir, eliminar, borrar, claro, etc.

Con una IEnumerable<T>, el consumidor API para crear una "bolsa" de artículos, sin especial orden, sin índice y sin métodos para modificar la colección.

Existen algunas circunstancias limitadas en las que es posible que desee devolver un IQueryable<T>, pero a menudo son específicas para generar consultas poco a poco.

En general, me gustaría por defecto a IEnumerable<T>. Podría usar un List<T> como campo de respaldo, pero solo quiero permitirle al usuario Add(), por ejemplo, no eliminar, borrar o cualquier otra cosa, por lo que declaro un public void Add<T>(T item) que simplemente agrega el elemento a la lista del campo de respaldo.

+0

No se olvide de ICollection si no necesita índices. IList implementa ICollection y simplemente agrega un indexador, IndexOf (elemento T), Insert (índice int, elemento T) y RemoveAt (índice int). –

1

Además de lo que otros han dicho, quiero agregar que nunca debe devolver IQueryable a menos que esté recuperando los objetos de algún origen de datos remoto. IQueryable es un objeto de consulta que obtendrá los resultados en nombre de la persona que llama. Cuando se utiliza LINQ, un IQuearyable usualmente tomará un árbol de expresiones que se traducirá para su uso por parte de algún otro sistema (por ejemplo, el servidor Sql).

Ver http://weblogs.asp.net/fredriknormen/archive/2008/12/01/returning-iqueryable-lt-t-gt.aspx para más detalles.

5

Como en general solo devuelvo objetos inmutables (no modificables) desde propiedades/métodos, esta respuesta asume que desea hacer lo mismo.

No se olvide de ReadOnlyCollection<T> que devuelve una colección inmutable que todavía se puede acceder por índice.

Si está utilizando IEnumerable<T> y la liberación de su tipo en el desierto incontrolable, tenga cuidado con esto:

class MyClass { 
    private List<int> _list; 
    public IEnumerable<int> Numbers { 
     get { return _list; } 
    } 
} 

Como un usuario podría hacer esto y estropear el estado interno de la clase:

var list = (List<int>)myClass.Numbers; 
list.Add(123); 

Esto violaría la intención de solo lectura de la propiedad. En tales casos, el comprador debe tener este aspecto:

public IEnumerable<int> Numbers { 
     get { return new ReadOnlyCollection<int>(_list); } 
    } 

Alternativamente se podría llamar _list.ToReadOnly(). Lo escribí en su totalidad para mostrar el tipo.

Eso detendrá a cualquier persona que modifique su estado (a menos que use la reflexión, pero eso es muy difícil de detener a menos que cree colecciones inmutables como las que se usan en muchos lenguajes de programación funcionales, y esa es otra historia).

Si devuelve colecciones de solo lectura, es mejor que declare al miembro como ReadOnlyCollection<T> ya que entonces ciertas acciones se realizan más rápido (obtener recuento, acceder a elementos por índice, copiar a otra colección).

Personalmente me gustaría ver el marco incluir y utilizar una interfaz de esta manera:

public interface IReadOnlyCollection<T> : IEnumerable<T> 
{ 
    T this[int index] { get; } 
    int Count { get; } 
    bool Contains(T item); 
    void CopyTo(T[] array, int arrayIndex); 
    int IndexOf(T item); 
} 

, usted puede obtener todas estas funciones utilizando métodos de extensión en la parte superior de IEnumerable<T> pero no son tan performante.

Cuestiones relacionadas