Estou confuso sobre qual tipo de coleção devo retornar dos meus métodos e propriedades da API pública.
As coleções que eu tenho em mente são IList
, ICollection
e Collection
.
O retorno de um desses tipos sempre é preferido em relação aos outros, ou depende da situação específica?
c#
.net
generics
collections
Rocky Singh
fonte
fonte
Respostas:
Geralmente, você deve retornar um tipo o mais geral possível, ou seja, aquele que conheça apenas os dados retornados que o consumidor precisa usar. Dessa forma, você tem maior liberdade para alterar a implementação da API, sem quebrar o código que a está usando.
Considere também a
IEnumerable<T>
interface como tipo de retorno. Se o resultado for apenas iterado, o consumidor não precisará mais do que isso.fonte
Count
método verifica o tipo real da coleção, portanto ele usará as propriedadesLength
ouCount
para alguns tipos conhecidos, como matrizes eICollection
mesmo quando você o fornecer comoIEnumerable
.Thing<T>
implementa,IList<T>
mas não a não genéricaICollection
, chamarIEnumerable<Cat>.Count()
umThing<Cat>
seria rápido, mas chamarIEnumerable<Animal>.Count()
seria lento (já que o método de extensão procuraria, e não encontraria, uma implementação deICollection<Cat>
) Se a classe implementasse o não genéricoICollection
, no entanto,IEnumerable<Animal>.Count
encontraria e usaria isso.ICollection<T>
é uma interface que expõe semântica de recolha, tais comoAdd()
,Remove()
eCount
.Collection<T>
é uma implementação concreta daICollection<T>
interface.IList<T>
é essencialmente umICollection<T>
com acesso baseado em ordem aleatória.Nesse caso, você deve decidir se seus resultados requerem ou não semânticas de lista, como indexação baseada em pedidos (depois use
IList<T>
) ou se você só precisa retornar uma "bolsa" não ordenada de resultados (então useICollection<T>
).fonte
Collection<T>
implementaIList<T>
e não apenasICollection<T>
.IEnumerable<T>
são ordenadas. O que distingueIList<T>
a partir deICollection<T>
é que ele oferece aos membros a trabalhar com índices. Por exemplolist[5]
, funciona, mascollection[5]
não compila.IList<T>
. Há muitas coleções ordenadas que não.IList<T>
trata-se de acesso indexado rápido.IEnumerable<T>
são encomendadas? TakeHashSet<T>
- ele implementa,IEnumerable<T>
mas claramente não está ordenado. Ou seja, você não tem influência na ordem dos itens e essa ordem pode mudar a qualquer momento, se a tabela de hash interna for reorganizada.A principal diferença entre
IList<T>
eICollection<T>
é queIList<T>
permite acessar elementos por meio de um índice.IList<T>
descreve tipos do tipo matriz. Os elementos em umICollection<T>
só podem ser acessados por enumeração. Ambos permitem a inserção e exclusão de elementos.Se você precisar enumerar apenas uma coleção,
IEnumerable<T>
é preferível. Tem duas vantagens sobre as outras:Ele não permite alterações na coleção (mas não nos elementos, se eles são do tipo de referência).
Ele permite a maior variedade possível de fontes, incluindo enumerações que são geradas algoritmicamente e não são coleções.
Collection<T>
é uma classe base que é útil principalmente para implementadores de coleções. Se você expô-lo em interfaces (APIs), muitas coleções úteis não derivadas serão excluídas.Uma desvantagem
IList<T>
é que as matrizes a implementam, mas não permitem adicionar ou remover itens (ou seja, você não pode alterar o comprimento da matriz). Uma exceção será lançada se você chamarIList<T>.Add(item)
uma matriz. A situação é um pouco neutralizada, poisIList<T>
possui uma propriedade booleanaIsReadOnly
que você pode verificar antes de tentar fazer isso. Mas, aos meus olhos, isso ainda é uma falha de design na biblioteca . Portanto, eu usoList<T>
diretamente, quando a possibilidade de adicionar ou remover itens é necessária.fonte
Collection
,ReadOnlyCollection
ouKeyedCollection
. E issoList
não deve ser devolvido.IList<T>
, não há como garantir que o método não seja chamado com uma matriz como parâmetro.IList<T>
é a interface base para todas as listas genéricas. Por ser uma coleção ordenada, a implementação pode decidir sobre a ordem, variando de ordem classificada a ordem de inserção. Além disso,Ilist
possui a propriedade Item que permite que os métodos leiam e editem entradas na lista com base em seu índice. Isso possibilita inserir, remover um valor na / da lista em um índice de posição.Além disso
IList<T> : ICollection<T>
, todos os métodos deICollection<T>
também estão disponíveis aqui para implementação.ICollection<T>
é a interface base para todas as coleções genéricas. Ele define tamanho, enumeradores e métodos de sincronização. Você pode adicionar ou remover um item em uma coleção, mas não pode escolher em qual posição ele ocorre devido à ausência da propriedade de índice.Collection<T>
fornece uma implementação paraIList<T>
,IList
eIReadOnlyList<T>
.Se você usar um tipo de interface mais restrito, como em
ICollection<T>
vez deIList<T>
, protege seu código contra as alterações. Se você usar um tipo de interface mais amplo, comoIList<T>
, você corre o risco de quebrar as alterações de código.Citando de uma fonte ,
fonte
O retorno de um tipo de interface é mais geral, portanto (sem informações adicionais sobre seu caso de uso específico), eu me inclinaria para isso. Se você deseja expor o suporte à indexação, escolha
IList<T>
, caso contrário,ICollection<T>
será suficiente. Por fim, se você deseja indicar que os tipos retornados são somente leitura, escolhaIEnumerable<T>
.E, caso você não tenha lido antes, Brad Abrams e Krzysztof Cwalina escreveram um ótimo livro intitulado "Diretrizes de design de estrutura: convenções, expressões idiomáticas e padrões para bibliotecas .NET reutilizáveis" (você pode baixar um resumo aqui ).
fonte
IEnumerable<T>
é somente leitura? Claro, mas ao executá-lo novamente em um contexto de repositório, mesmo depois de executar o ToList, o thread não é seguro. O problema é quando a fonte de dados original obtém o GC e o IEnumarble.ToList () não funciona em um contexto multithread. Ele funciona de maneira linear, porque o GC é chamado depois que você faz o trabalho, masasync
agora usa os dias de 4.5+ IEnumarbale está se tornando uma dor de bola.Existem alguns assuntos que vêm dessa pergunta:
Convém destacar que é uma API orientada a objetos
interfaces versus classes
Se você não tem muita experiência com interfaces, recomendo manter as aulas. Eu vejo muitas vezes os desenvolvedores pulando para interfaces, mesmo que não seja necessário.
E, finalize com um design de interface ruim, em vez de um bom design de classe, que, a propósito, pode eventualmente ser migrado para um bom design de interface ...
Você verá muitas interfaces na API, mas não se apresse, se não precisar.
Você acabará aprendendo como aplicar interfaces ao seu código.
qual classe específica, de várias classes iguais, coleção, lista, matriz?
Existem várias classes em c # (dotnet) que podem ser trocadas. Como já mencionado, se você precisar de algo de uma classe mais específica, como "CanBeSortedClass", torne explícito na sua API.
O usuário da API realmente precisa saber que sua classe pode ser classificada ou aplicar algum formato aos elementos? Em seguida, use "CanBeSortedClass" ou "ElementsCanBePaintedClass", caso contrário, use "GenericBrandClass".
Caso contrário, use uma classe mais geral.
Classes de coleção comuns versus coleções de subitens ("genéricos")
Você verá que existem classes que contêm outros elementos e pode especificar que todos os elementos devem ser de um tipo específico.
Coleções genéricas são aquelas classes que você pode usar a mesma coleção, para vários aplicativos de código, sem precisar criar uma nova coleção, para cada novo tipo de subitem, como este: Coleção .
Seu usuário da API precisará de um tipo muito específico, o mesmo para todos os elementos?
Use algo parecido
List<WashingtonApple>
.Seu usuário da API precisará de vários tipos relacionados?
Expor
List<Fruit>
para o seu API, e usarList<Orange>
List<Banana>
,List<Strawberry>
internamente, ondeOrange
,Banana
eStrawberry
são descendentes deFruit
.Seu usuário da API precisará de uma coleção de tipos genéricos?
Use
List
, onde todos os itens sãoobject
.Felicidades.
fonte