Diretrizes para coleções
Nota
Este conteúdo é reimpresso com permissão da Pearson Education, Inc., a partir de Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition. Essa edição foi publicada em 2008 e, desde então, o livro foi totalmente revisto na terceira edição. Algumas das informações nesta página podem estar desatualizadas.
Qualquer tipo projetado especificamente para manipular um grupo de objetos com alguma característica comum pode ser considerado uma coleção. É quase sempre apropriado para esses tipos implementar IEnumerable ou IEnumerable<T>, por isso, nesta seção, consideramos apenas os tipos que implementam uma ou ambas as interfaces como coleções.
❌ NÃO use coleções com tipagem fraca em APIs públicas.
O tipo de todos os valores de retorno e parâmetros que representam itens de coleção deve ser o tipo de item exato, não qualquer um de seus tipos base (isso se aplica apenas aos membros públicos da coleção).
❌ NÃO use ArrayList ou List<T> em APIs públicas.
Esses tipos são estruturas de dados projetadas para serem usadas em implementação interna, não em APIs públicas. List<T>
é otimizado para desempenho e potência ao custo da limpeza das APIs e flexibilidade. Por exemplo, se você retornar List<T>
, você nunca será capaz de receber notificações quando o código do cliente modifica a coleção. Além disso, List<T>
expõe muitos membros, como BinarySearch, que não são úteis ou aplicáveis em muitos cenários. As duas seções a seguir descrevem tipos (abstrações) projetados especificamente para uso em APIs públicas.
❌ NÃO use Hashtable
ou Dictionary<TKey,TValue>
em APIs públicas.
Esses tipos são estruturas de dados projetadas para serem usadas na implementação interna. As APIs públicas devem usar IDictionary, IDictionary <TKey, TValue>
ou um tipo personalizado implementando uma ou ambas as interfaces.
❌ NÃO use IEnumerator<T>, IEnumerator, ou qualquer outro tipo que implemente qualquer uma dessas interfaces, exceto como o tipo de retorno de um GetEnumerator
método.
Tipos que retornam enumeradores de métodos diferentes não GetEnumerator
podem ser usados com a foreach
instrução.
❌ NÃO implemente ambos IEnumerator<T>
e IEnumerable<T>
no mesmo tipo. O mesmo se aplica às interfaces IEnumerator
não genéricas e IEnumerable
.
Parâmetros de coleta
✔️ DO use o tipo menos especializado possível como um tipo de parâmetro. A maioria dos membros que tomam coleções como parâmetros usam a IEnumerable<T>
interface.
❌ EVITE usar ICollection<T> ou ICollection como parâmetro apenas para acessar o Count
imóvel.
Em vez disso, considere usar IEnumerable<T>
ou IEnumerable
e verificar dinamicamente se o objeto implementa ICollection<T>
ou ICollection
.
Propriedades de coleta e valores de retorno
❌ NÃO forneça propriedades de coleção configuráveis.
Os usuários podem substituir o conteúdo da coleção limpando a coleção primeiro e, em seguida, adicionando o novo conteúdo. Se a substituição de toda a coleção for um cenário comum, considere fornecer o AddRange
método na coleção.
✔️ DO use Collection<T>
ou uma subclasse de para propriedades ou valores de retorno que representam coleções de Collection<T>
leitura/gravação.
Se Collection<T>
não atender a algum requisito (por exemplo, a coleção não deve implementar IList), use uma coleção personalizada implementando IEnumerable<T>
, ICollection<T>
ou IList<T>.
✔️ DO use ReadOnlyCollection<T>, uma subclasse de , ou, em casos IEnumerable<T>
raros, para propriedades ou valores de ReadOnlyCollection<T>
retorno que representam coleções somente leitura.
Em geral, prefira ReadOnlyCollection<T>
. Se ele não atender a algum requisito (por exemplo, a coleção não deve implementar IList
), use uma coleção personalizada implementando IEnumerable<T>
, ICollection<T>
ou IList<T>
. Se você implementar uma coleção somente leitura personalizada, implemente ICollection<T>.IsReadOnly
para retornar true
.
Nos casos em que você tem certeza de que o único cenário ao qual você vai querer oferecer suporte é a iteração somente para encaminhamento, você pode simplesmente usar IEnumerable<T>
o .
✔️ CONSIDERE o uso de subclasses de coleções base genéricas em vez de usar as coleções diretamente.
Isso permite um nome melhor e a adição de membros auxiliares que não estão presentes nos tipos de coleção base. Isso é especialmente aplicável a APIs de alto nível.
✔️ CONSIDERE retornar uma subclasse de Collection<T>
ou ReadOnlyCollection<T>
de métodos e propriedades muito comumente usados.
Isso possibilitará que você adicione métodos auxiliares ou altere a implementação da coleção no futuro.
✔️ CONSIDERE o uso de uma coleção com chaves se os itens armazenados na coleção tiverem chaves exclusivas (nomes, IDs, etc.). Coleções com chave são coleções que podem ser indexadas por um inteiro e uma chave e geralmente são implementadas herdando de KeyedCollection<TKey,TItem>
.
As coleções com chaves geralmente têm pegadas de memória maiores e não devem ser usadas se a sobrecarga de memória superar os benefícios de ter as chaves.
❌ NÃO retorne valores nulos de propriedades de coleção ou de métodos que retornam coleções. Em vez disso, retorne uma coleção vazia ou uma matriz vazia.
A regra geral é que coleções ou matrizes nulas e vazias (0 item) devem ser tratadas da mesma forma.
Snapshots versus coleções ao vivo
As coleções que representam um estado em algum momento são chamadas de coleções de instantâneos. Por exemplo, uma coleção contendo linhas retornadas de uma consulta de banco de dados seria um instantâneo. As coleções que sempre representam o estado atual são chamadas de coleções dinâmicas. Por exemplo, uma coleção de ComboBox
itens é uma coleção ativa.
❌ NÃO retorne coleções de instantâneos de propriedades. As propriedades devem retornar coleções ativas.
Os proprietários devem ser operações muito leves. O retorno de um instantâneo requer a criação de uma cópia de uma coleção interna em uma operação O(n).
✔️ DO use uma coleção de instantâneos ou uma live IEnumerable<T>
(ou seu subtipo) para representar coleções que são voláteis (ou seja, que podem ser alteradas sem modificar explicitamente a coleção).
Em geral, todas as coleções que representam um recurso compartilhado (por exemplo, arquivos em um diretório) são voláteis. Tais coleções são muito difíceis ou impossíveis de implementar como coleções ao vivo, a menos que a implementação seja simplesmente um enumerador somente para frente.
Escolhendo entre matrizes e coleções
✔️ PREFIRA coleções em vez de matrizes.
As coleções fornecem mais controle sobre o conteúdo, podem evoluir ao longo do tempo e são mais utilizáveis. Além disso, o uso de arrays para cenários somente leitura é desencorajado porque o custo de clonagem do array é proibitivo. Estudos de usabilidade mostraram que alguns desenvolvedores se sentem mais confortáveis usando APIs baseadas em coleção.
No entanto, se você estiver desenvolvendo APIs de baixo nível, talvez seja melhor usar matrizes para cenários de leitura-gravação. Os arrays têm um espaço de memória menor, o que ajuda a reduzir o conjunto de trabalho, e o acesso aos elementos em um array é mais rápido porque é otimizado pelo tempo de execução.
✔️ CONSIDERE o uso de matrizes em APIs de baixo nível para minimizar o consumo de memória e maximizar o desempenho.
✔️ DO use matrizes de bytes em vez de coleções de bytes.
❌ NÃO use matrizes para propriedades se a propriedade tiver que retornar uma nova matriz (por exemplo, uma cópia de uma matriz interna) toda vez que o getter de propriedade for chamado.
Implementando coleções personalizadas
✔️ CONSIDERE herdar de Collection<T>
, ReadOnlyCollection<T>
ou KeyedCollection<TKey,TItem>
ao projetar novas coleções.
✔️ DO implementar IEnumerable<T>
ao projetar novas coleções. Considere implementar ICollection<T>
ou mesmo IList<T>
onde fizer sentido.
Ao implementar essa coleção personalizada, siga o padrão de API estabelecido por Collection<T>
e ReadOnlyCollection<T>
o mais próximo possível. Ou seja, implementar os mesmos membros explicitamente, nomear os parâmetros como essas duas coleções nomeá-los, e assim por diante.
✔️ CONSIDERE a implementação de interfaces de coleta não genéricas (IList
e ICollection
) se a coleção geralmente for passada para APIs que usam essas interfaces como entrada.
❌ EVITE implementar interfaces de coleção em tipos com APIs complexas não relacionadas ao conceito de uma coleção.
❌ NÃO herdar de coleções base não genéricas, como CollectionBase
. Use Collection<T>
, ReadOnlyCollection<T>
e KeyedCollection<TKey,TItem>
em vez disso.
Nomeando coleções personalizadas
As coleções (tipos que implementamIEnumerable
) são criadas principalmente por dois motivos: (1) para criar uma nova estrutura de dados com operações específicas da estrutura e, muitas vezes, características de desempenho diferentes das estruturas de dados existentes (por exemplo, List<T>, , LinkedList<T>Stack<T>), e (2) para criar uma coleção especializada para armazenar um conjunto específico de itens (por exemplo, StringCollection). As estruturas de dados são mais frequentemente utilizadas na implementação interna de aplicações e bibliotecas. Coleções especializadas devem ser expostas principalmente em APIs (como tipos de propriedade e parâmetro).
✔️ USE o sufixo "Dicionário" em nomes de abstrações implementando IDictionary
ou IDictionary<TKey,TValue>
.
✔️ DO use o sufixo "Coleção" em nomes de tipos implementando IEnumerable
(ou qualquer um de seus descendentes) e representando uma lista de itens.
✔️ DO use o nome apropriado da estrutura de dados para estruturas de dados personalizadas.
❌ EVITE usar quaisquer sufixos que impliquem uma implementação específica, como "LinkedList" ou "Hashtable", em nomes de abstrações de coleção.
✔️ CONSIDERE prefixar nomes de coleção com o nome do tipo de item. Por exemplo, uma coleção que armazena itens do tipo Address
(implementando IEnumerable<Address>
) deve ser nomeada AddressCollection
. Se o tipo de item for uma interface, o prefixo "I" do tipo de item pode ser omitido. Assim, uma coleção de itens pode ser chamada DisposableCollection
de IDisposable .
✔️ CONSIDERE o uso do prefixo "ReadOnly" em nomes de coleções somente leitura se uma coleção gravável correspondente puder ser adicionada ou já existir na estrutura.
Por exemplo, uma coleção somente leitura de cadeias de caracteres deve ser chamada ReadOnlyStringCollection
de .
© Partes 2005, 2009 Microsoft Corporation. Todos os direitos reservados.
Reimpresso com permissão da Pearson Education, Inc., de Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition por Krzysztof Cwalina e Brad Abrams, publicado em 22 de outubro de 2008 por Addison-Wesley Professional como parte da Microsoft Windows Development Series.