IEnumerable vs IReadonlyCollection vs ReadonlyCollection por expor um membro da lista
Passei algumas horas pensando no assunto de expor os membros da lista. Em uma pergunta semelhante à minha, John Skeet deu uma excelente resposta. Por favor, sinta-se livre para dar uma olhada.
ReadOnlyCollection ou IEnumerable para expor coleções de membros?
Geralmente sou bastante paranóico em expor listas, especialmente se você estiver desenvolvendo uma API.
Eu sempre usei o IEnumerable para expor listas, pois é bastante seguro e oferece muita flexibilidade. Deixe-me usar um exemplo aqui
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IEnumerable<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
Qualquer pessoa que codifique contra um IEnumerable é bastante segura aqui. Se eu decidir mais tarde usar uma lista ordenada ou algo assim, nenhum código será quebrado e ainda será bom. A desvantagem disso é IEnumerable, que pode ser convertida em uma lista fora desta classe.
Por esse motivo, muitos desenvolvedores usam o ReadOnlyCollection para expor um membro. Isso é bastante seguro, pois nunca pode ser lançado de volta para uma lista. Para mim, eu prefiro o IEnumerable, pois fornece mais flexibilidade, caso eu queira implementar algo diferente de uma lista.
Eu vim com uma nova idéia que eu gosto mais. Usando IReadOnlyCollection
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return new ReadOnlyCollection<WorkItem>(this.workItems);
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
Eu sinto que isso retém parte da flexibilidade do IEnumerable e é bastante encapsulado.
Publiquei esta pergunta para obter alguma contribuição sobre minha ideia. Você prefere esta solução ao IEnumerable? Você acha que é melhor usar um valor de retorno concreto de ReadOnlyCollection? Este é um debate e quero tentar ver quais são as vantagens / desvantagens que todos nós podemos apresentar.
Agradecemos antecipadamente a sua contribuição.
EDITAR
Antes de tudo, obrigado a todos por contribuírem muito para a discussão aqui. Eu certamente aprendi muito com cada um e gostaria de agradecer sinceramente.
Estou adicionando alguns cenários e informações extras.
Existem algumas armadilhas comuns com IReadOnlyCollection e IEnumerable.
Considere o exemplo abaixo:
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
O exemplo acima pode ser convertido de volta para uma lista e alterado, mesmo que a interface seja somente leitura. A interface, apesar de ser homônima, não garante a imutabilidade. Cabe a você fornecer uma solução imutável; portanto, você deve retornar um novo ReadOnlyCollection. Ao criar uma nova lista (essencialmente uma cópia), o estado do seu objeto é seguro.
Richiban diz o melhor em seu comentário: a interface garante apenas o que algo pode fazer, não o que não pode fazer
Veja abaixo um exemplo.
public IEnumerable<WorkItem> WorkItems
{
get
{
return new List<WorkItem>(this.workItems);
}
}
O acima pode ser convertido e alterado, mas seu objeto ainda é imutável.
Outra declaração fora da caixa seria classes de coleção. Considere o seguinte:
public class Bar : IEnumerable<string>
{
private List<string> foo;
public Bar()
{
this.foo = new List<string> { "123", "456" };
}
public IEnumerator<string> GetEnumerator()
{
return this.foo.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
A classe acima pode ter métodos para mudar o idioma da maneira que você deseja, mas seu objeto nunca pode ser convertido para uma lista de qualquer tipo e alterado.
Carsten Führmann faz uma observação fantástica sobre as declarações de retorno de rendimento no IEnumerables.
Obrigado a todos mais uma vez.