No meu código na necessidade de usar IEnumerable<>
várias vezes, portanto, obter o erro Resharper de "Enumeração múltipla possível de IEnumerable
".
Código de amostra:
public List<object> Foo(IEnumerable<object> objects)
{
if (objects == null || !objects.Any())
throw new ArgumentException();
var firstObject = objects.First();
var list = DoSomeThing(firstObject);
var secondList = DoSomeThingElse(objects);
list.AddRange(secondList);
return list;
}
- Posso alterar o
objects
parâmetroList
e evitar a possível enumeração múltipla, mas não obtenho o objeto mais alto que posso manipular. - Outra coisa que posso fazer é converter o
IEnumerable
paraList
no início do método:
public List<object> Foo(IEnumerable<object> objects)
{
var objectList = objects.ToList();
// ...
}
Mas isso é apenas estranho .
O que você faria neste cenário?
fonte
IReadOnlyCollection(T)
(novo no .net 4.5) como a melhor interface para transmitir a idéia de que é umaIEnumerable(T)
que deve ser enumerada várias vezes. Como esta resposta afirma,IEnumerable(T)
ela é tão genérica que pode até se referir a conteúdo não redefinível que não pode ser enumerado novamente sem efeitos colaterais. MasIReadOnlyCollection(T)
implica re-iterabilidade..ToList()
no início do método, primeiro precisará verificar se o parâmetro não é nulo. Isso significa que você obterá dois lugares onde você lançará uma ArgumentException: se o parâmetro for nulo e se não houver nenhum item.IReadOnlyCollection<T>
vsIEnumerable<T>
e postei uma pergunta sobre o usoIReadOnlyCollection<T>
como parâmetro e em quais casos. Penso que a conclusão a que cheguei é a lógica a seguir. Que ele deve ser usado onde a enumeração é esperada, não necessariamente onde possa haver enumeração múltipla.Se seus dados sempre serão repetíveis, talvez não se preocupe. No entanto, você também pode desenrolá-lo - isso é especialmente útil se os dados recebidos puderem ser grandes (por exemplo, leitura do disco / rede):
Observe que mudei um pouco a semântica de DoSomethingElse, mas isso é principalmente para mostrar o uso desenrolado. Você pode envolver novamente o iterador, por exemplo. Você também pode transformá-lo em um bloco iterador, o que pode ser bom; então não há
list
- e você fariayield return
os itens à medida que os obtém, em vez de adicionar a uma lista a ser devolvida.fonte
O uso
IReadOnlyCollection<T>
ouIReadOnlyList<T>
na assinatura do método, em vez deIEnumerable<T>
, tem a vantagem de tornar explícito que você pode precisar verificar a contagem antes de iterar ou iterar várias vezes por algum outro motivo.No entanto, eles têm uma enorme desvantagem que causará problemas se você tentar refatorar seu código para usar interfaces, por exemplo, para torná-lo mais testável e amigável ao proxy dinâmico. O ponto principal é que
IList<T>
não herda deIReadOnlyList<T>
e da mesma forma para outras coleções e suas respectivas interfaces somente leitura. (Em resumo, isso ocorre porque o .NET 4.5 queria manter a compatibilidade ABI com versões anteriores. Mas eles nem aproveitaram a oportunidade para mudar isso no núcleo do .NET. )Isso significa que, se você obtém uma
IList<T>
parte de algum programa e deseja passá-lo para outra parte que espera umaIReadOnlyList<T>
, não pode! No entanto, você pode passar umIList<T>
como umIEnumerable<T>
.No final,
IEnumerable<T>
é a única interface somente leitura suportada por todas as coleções .NET, incluindo todas as interfaces de coleção. Qualquer outra alternativa voltará a mordê-lo quando você perceber que se excluiu de algumas opções de arquitetura. Então eu acho que é o tipo apropriado para usar em assinaturas de funções para expressar que você deseja apenas uma coleção somente leitura.(Observe que você sempre pode escrever um
IReadOnlyList<T> ToReadOnly<T>(this IList<T> list)
método de extensão que faça a conversão simples, se o tipo subjacente suportar as duas interfaces, mas você deve adicioná-lo manualmente em qualquer lugar ao refatorar, ondeIEnumerable<T>
sempre é compatível.)Como sempre, isso não é absoluto; se você estiver escrevendo um código pesado para o banco de dados, em que a enumeração múltipla acidental seria um desastre, talvez prefira uma troca diferente.
fonte
Se o objetivo é realmente evitar várias enumerações, é melhor ler a resposta de Marc Gravell, mas mantendo a mesma semântica, você pode simplesmente remover o redundante
Any
e asFirst
chamadas e seguir com:Observe que isso pressupõe que você
IEnumerable
não é genérico ou, pelo menos, está restrito a ser um tipo de referência.fonte
objects
ainda está sendo passado para DoSomethingElse, que está retornando uma lista, portanto a possibilidade de você ainda enumerar duas vezes ainda está lá. De qualquer maneira, se a coleção era uma consulta LINQ to SQL, você acessava o banco de dados duas vezes.First
lançará uma exceção para uma lista vazia. Eu não acho possível dizer nunca lançar exceção na lista vazia ou sempre lançar exceção na lista vazia. Depende ...Normalmente sobrecarrego meu método com IEnumerable e IList nessa situação.
Tomo o cuidado de explicar nos comentários resumidos dos métodos que a chamada IEnumerable executará um .ToList ().
O programador pode optar por .ToList () em um nível superior se várias operações estiverem sendo concatenadas e depois chamar a sobrecarga IList ou deixar minha sobrecarga IEnumerable cuidar disso.
fonte
Se você precisar apenas verificar o primeiro elemento, poderá espiá-lo sem iterar toda a coleção:
fonte
Primeiro, esse aviso nem sempre significa muito. Normalmente, eu o desabilito depois de me certificar de que não é um gargalo de desempenho. Significa apenas que a
IEnumerable
avaliação é feita duas vezes, o que geralmente não é um problema, a menos que o processoevaluation
demore muito. Mesmo que demore muito, nesse caso, você está usando apenas um elemento na primeira vez.Nesse cenário, você também pode explorar ainda mais os poderosos métodos de extensão linq.
É possível avaliar apenas
IEnumerable
uma vez neste caso com alguma trabalheira, mas perfil primeiro e ver se é realmente um problema.fonte
IEnumerables
resultados diferentes a cada iteração ou que levam muito tempo para iterar. Veja a resposta de Marc Gravells - Ele também declara em primeiro lugar "talvez não se preocupe". O problema é que: muitas vezes você vê isso e não tem com o que se preocupar.