remarks - see cref c#
Colección<T> versus Lista<T> ¿qué debería usar en sus interfaces? (9)
¡Un objetivo es hacer que el código / diseño sea más flexible y extensible!
El código se ve a continuación:
namespace Test
{
public interface IMyClass
{
List<IMyClass> GetList();
}
public class MyClass : IMyClass
{
public List<IMyClass> GetList()
{
return new List<IMyClass>();
}
}
}
Cuando ejecuto el Análisis de código, obtengo la siguiente recomendación.
Advertencia 3 CA1002: Microsoft.Design: cambie ''List'' en ''IMyClass.GetList ()'' para usar Collection, ReadOnlyCollection o KeyedCollection
¿Cómo debería solucionar esto y qué es una buena práctica aquí?
Algo para agregar, aunque ha pasado mucho tiempo desde que se hizo esta pregunta.
Cuando su tipo de lista se deriva de List<T>
lugar de Collection<T>
, no puede implementar los métodos virtuales protegidos que implementa Collection<T>
. Lo que esto significa es que el tipo derivado no puede responder en caso de que se realicen modificaciones en la lista. Esto se debe a que List<T>
asume que eres consciente cuando agregas o quitas elementos. Poder responder a las notificaciones es una sobrecarga y, por lo tanto, List<T>
no lo ofrece.
En los casos en que el código externo tenga acceso a su colección, es posible que no tenga el control de cuándo se agregará o eliminará un elemento. Por lo tanto Collection<T>
proporciona una forma de saber cuándo se modificó su lista.
Bueno, la clase Collection es en realidad una clase de envoltorio alrededor de otras colecciones para ocultar sus detalles de implementación y otras características. Creo que esto tiene algo que ver con el patrón de codificación que oculta la propiedad en los lenguajes orientados a objetos.
Creo que no debería preocuparse por eso, pero si realmente quiere agradar a la herramienta de análisis de código, solo haga lo siguiente:
//using System.Collections.ObjectModel;
Collection<MyClass> myCollection = new Collection<MyClass>(myList);
En este tipo de casos, generalmente trato de exponer la menor cantidad de implementos que se necesita. Si los consumidores no necesitan saber que realmente está usando una lista, entonces no es necesario que devuelva una lista. Al regresar cuando Microsoft sugiere una Colección, oculta el hecho de que está utilizando una lista de los consumidores de su clase y los aísla de un cambio interno.
No creo que nadie haya respondido la parte de "por qué" todavía ... así que aquí va. La razón por la que "debería" usar una Collection<T>
lugar de una List<T>
es porque si expone una List<T>
, cualquiera que tenga acceso a su objeto puede modificar los elementos en la lista. Mientras que Collection<T>
se supone que indica que está haciendo sus propios métodos "Agregar", "Eliminar", etc.
Probablemente no tengas que preocuparte por eso, porque probablemente solo estés codificando la interfaz (o quizás algunos colegas). Aquí hay otro ejemplo que podría tener sentido.
Si tiene una matriz pública, por ejemplo:
public int[] MyIntegers { get; }
Podrías pensar que debido a que solo hay un "acceso directo" para que nadie pueda meterse con los valores, eso no es cierto. Cualquiera puede cambiar los valores allí dentro así:
someObject.MyIngegers[3] = 12345;
Personalmente, solo usaría List<T>
en la mayoría de los casos. Pero si está diseñando una biblioteca de clases que va a distribuir a desarrolladores aleatorios, y necesita confiar en el estado de los objetos ... entonces querrá hacer su propia Colección y bloquearla desde allí: )
No veo ningún problema para devolver algo así como
this.InternalData.Filter(crteria).ToList();
Si devuelvo una copia desconectada de datos internos, o un resultado separado de una consulta de datos, puedo devolver de manera segura List<TItem>
sin exponer ninguno de los detalles de implementación, y permitir el uso de los datos devueltos de la manera conveniente.
Pero esto depende del tipo de consumidor que espero: si esto es algo así como una grilla de datos, prefiero devolver IEnumerable<TItem>
que será la lista de elementos copiados de todos modos en la mayoría de los casos :)
Para responder a la pregunta "por qué" de por qué no List<T>
, las razones son el futuro y la simplicidad de API.
Para el futuro
List<T>
no está diseñado para ser fácilmente extensible subclasificándolo; está diseñado para ser rápido para implementaciones internas. Notará que los métodos que contiene no son virtuales y, por lo tanto, no pueden anularse, y no hay enganches en sus operaciones Add
/ Insert
/ Remove
.
Esto significa que si necesita modificar el comportamiento de la colección en el futuro (por ejemplo, para rechazar objetos nulos que la gente intenta agregar, o para realizar un trabajo adicional cuando esto sucede, como la actualización de su estado de clase), entonces necesita cambiar el tipo de la colección regresas a una subclase, que será un cambio de interfaz de última hora (por supuesto, cambiar la semántica de cosas como no permitir null también puede ser un cambio de interfaz, pero cosas como actualizar tu estado de clase interno no lo serían).
Entonces al devolver una clase que se puede subclasificar fácilmente, como Collection<T>
o una interfaz como IList<T>
, ICollection<T>
o IEnumerable<T>
, puede cambiar su implementación interna para que sea un tipo de colección diferente para satisfacer sus necesidades, sin romper el código de los consumidores porque todavía se puede devolver como el tipo que están esperando.
Simplicidad API
List<T>
contiene muchas operaciones útiles, como BinarySearch
, Sort
, etc. Sin embargo, si se trata de una colección que está exponiendo, es probable que controle la semántica de la lista y no los consumidores. Entonces, aunque su clase internamente pueda necesitar estas operaciones, es muy poco probable que los consumidores de su clase quieran (o incluso deberían) llamarlas.
Como tal, al ofrecer una clase o interfaz de recopilación más simple, se reduce el número de miembros que ven los usuarios de su API y se les facilita el uso.
Se trata principalmente de abstraer sus propias implementaciones en lugar de exponer el objeto List para ser manipulado directamente.
No es una buena práctica dejar que otros objetos (o personas) modifiquen el estado de sus objetos directamente. Piensa en los buscadores de propiedades / setters.
Colección -> Para colección normal
ReadOnlyCollection -> Para colecciones que no deberían modificarse
KeyedCollection -> Cuando quiere diccionarios en su lugar.
Cómo solucionarlo depende de lo que quiere que haga su clase y el propósito del método GetList (). ¿Puedes elaborar?
Yo personalmente declaro que devolverá una interfaz en lugar de una colección concreta. Si realmente quieres acceso a la lista, usa IList<T>
. De lo contrario, considere ICollection<T>
e IEnumerable<T>
.