Pergunta

Existe um problema com o uso IEnumerable<T> como um tipo de retorno? FxCop reclama retornando List<T> (aconselha retornando Collection<T> vez).

Bem, eu sempre fui guiado por uma regra "aceitar o mínimo que você pode, mas devolver o máximo."

A partir deste ponto de vista, retornando IEnumerable<T> é uma coisa ruim, mas o que devo fazer quando eu quiser usar "recuperação preguiçoso"? Além disso, a palavra-chave yield é como um goodie.

Foi útil?

Solução

Esta é realmente uma questão em duas partes.

1) Existe inerentemente errado nada com retornando um IEnumerable

Sem nada. De fato, se você estiver usando C # iteradores este é o comportamento esperado. Convertendo-a em um List ou outro preventivamente classe de coleção não é uma boa idéia. Fazer isso é fazendo uma suposição sobre o padrão de uso de seu interlocutor. Acho que não é uma boa idéia para assumir nada sobre o chamador. Eles podem ter boas razões por que eles querem um IEnumerable . Talvez eles querem convertê-lo em uma hierarquia coleção completamente diferente (caso em que uma conversão para a Lista é desperdiçado).

2) Existem circunstâncias em que pode ser preferível para retornar algo diferente de IEnumerable ?

Sim. Embora não seja uma grande idéia para assumir muito sobre o seu interlocutor, é perfeitamente aceitável para tomar decisões com base no seu próprio comportamento. Imagine um cenário em que você tinha um objeto de rosca-multi que foi fazendo fila pedidos em um objeto que estava sendo constantemente atualizado. Neste caso, retornando um IEnumerable raw é irresponsável. Assim que a coleção é modificado a enumeráveis ??é invalidado e causará um execption a ocorrer. Em vez disso, você poderia tirar um instantâneo da estrutura e retornar esse valor. Say em um List formulário. Neste caso, eu só iria devolver o objeto como a estrutura direta (ou interface).

Este é certamente o caso mais raro embora.

Outras dicas

Não, IEnumerable<T> é uma boa coisa para voltar aqui, já que tudo que você está prometendo é "uma sequência de (digitados) valores". Ideal para LINQ, etc, e perfeitamente utilizável.

O chamador pode facilmente colocar esses dados em uma lista (ou qualquer outro) -. Especialmente com LINQ (ToList, ToArray, etc)

Esta abordagem permite que você enrolar preguiçosamente volta valores, ao invés de ter a buffer todos os dados. Definitivamente um goodie. Escrevi-up outro truque IEnumerable<T> útil no outro dia também.

Sobre o seu princípio:. "Aceitar o mínimo que você pode, mas devolver o máximo"

A chave para gerenciar a complexidade de um grande programa é uma técnica chamada informações esconderijo . Se o seu método funciona através da construção de um List<T>, não é muitas vezes necessário para revelar este fato, retornando desse tipo. Se o fizer, então o seu interlocutor pode modificar a lista de eles voltarem. Isso elimina sua capacidade de fazer o cache, ou iteração preguiçoso com yield return.

Assim, um princípio é melhor para uma função a seguir é:. "Revelar o mínimo possível sobre como você trabalhar"

IEnumerable é bem por mim, mas tem alguns inconvenientes. O cliente tem de enumerar para obter os resultados. Não tem nenhuma maneira de verificar Conde etc. Lista é ruim porque você expõe muito controle; o cliente pode adicionar / remover etc. dele e que pode ser uma coisa ruim. Coleção parece ser a melhor compromomise, pelo menos na visão de FxCop. I hair uso o que parece apropriado no meu contexto (ex. Se eu quiser voltar a ler única coleção i expor coleção como tipo de retorno e retornar List.AsReadOnly () ou IEnumerable para avaliação preguiçosa através do rendimento etc.). Levá-lo em uma base caso a caso

"aceitar o mínimo que você pode, mas devolver o máximo" é o que eu defendo. Quando um método retorna um objeto, o que justificativas não temos para retornar o tipo real e limitar as capacidades do objeto, retornando um tipo de base. Este, porém, levanta uma questão como nós sabemos o que o "máximo" (tipo real) vai ser quando nós projetamos uma interface. A resposta é muito simples. Apenas em casos extremos onde o designer de interface está projetando uma interface aberta, que será implementada fora do aplicativo / componente, eles não saberiam o que o tipo de retorno real pode ser. Um designer inteligente deve sempre considerar o que o método deveria estar fazendo e que uma / tipo de retorno genérico ideal deveria ser.

por exemplo. Se eu estou projetando uma interface para recuperar um vector de objetos, e eu sei que a contagem de objetos retornados vão ser variável, eu sempre vou assumir um desenvolvedor inteligente irá sempre usar uma lista. Se os planos de alguém para retornar um array, eu questionar suas capacidades, a menos que ele / ela está apenas retornando os dados de outra camada que ele / ela não possui. E isso é provavelmente por isso que FxCop defende ICollection (base comum para List e Array).

O ser dito acima, existem algumas outras coisas a considerar

  • Se os dados retornados deve ser mutável ou imutável

  • Se os dados retornados ser compartilhado entre vários chamadores

Em relação aos avaliação preguiçosa LINQ estou certo 95% + C # usuários não entendem os intestacies. É tão não-oo ish. OO promove mudanças de estado concretas sobre invocações de método. avaliação preguiçosa LINQ promove mudanças de estado de tempo de execução no padrão de avaliação de expressão (os usuários não algo não-avançados sempre seguir).

Voltando IEnumerable é OK se você está realmente retornando apenas uma enumeração, e ela será consumida pelo seu interlocutor como tal.

Mas, como outros apontam, tem a desvantagem de que o chamador pode precisar para enumerar se ele precisa de qualquer outra informação (por exemplo, Count). O .NET método 3,5 extensão IEnumerable .Count irá enumerar nos bastidores, se o valor de retorno não implementa ICollection , que pode ser indesejável.

Muitas vezes eu retornar IList ou ICollection quando o resultado é uma coleção - internamente o seu método pode usar um List e quer devolvê-lo tal como está, ou retornar List .AsReadOnly se quer proteger contra a modificação (por exemplo, se você estiver cache da lista internamente). AFAIK FxCop é bastante feliz com qualquer um destes.

Um aspecto importante é que, quando você retornar a List<T> você é real retornando um referência . Isso torna possível para um chamador para manipular sua lista. Este é um-para o problema exemplo, uma camada de negócio comum que retorna um List<T> a uma camada de GUI.

Só porque você diz que está voltando não IEnumerable não significa que você não pode retornar um List. A idéia é reduzir o acoplamento desnecessário. Tudo o que o chamador deve se preocupar é obter uma lista de coisas, em vez do tipo exato de coleção usada para conter essa lista. Se você tem algo que é apoiado por uma matriz, em seguida, obter algo como Conde vai ser rápido de qualquer maneira.

Eu acho que sua própria orientação é grande - se você é capaz de ser mais específico sobre o que você está retornando sem um acerto de desempenho (você não tem que, por exemplo, construir uma lista fora de seu resultado), fazê-lo. Mas se a sua função legitimamente não sabe o tipo que vai encontrar, como se em algumas situações você estará trabalhando com uma lista e, em alguns com uma matriz, etc., em seguida, retornar IEnumerable é o "melhor" que você pode fazer . Pense nisso como o "maior múltiplo comum" de tudo que você pode querer voltar.

Não posso aceitar a resposta escolhida. Existem maneiras de lidar com o cenário descrito, mas utilizando uma lista ou qualquer outra coisa que o seu uso não é um deles. O momento em que o IEnumerable é devolvido você tem que assumir que o chamador pode fazer um foreach. Nesse caso, não importa se o tipo de concreto é lista ou espaguete. Na verdade, apenas a indexação é um problema especialmente se os itens são removidos.

Qualquer valor retornado é um instantâneo. Pode ser o conteúdo atual do IEnumerable caso em que se está em cache deve ser um clone da cópia em cache; se ele é suposto ser mais dinâmico (como os resuts de uma consulta SQL), em seguida, usar o retorno de rendimento; no entanto permitindo que o recipiente de mutação à vontade e fornecimento de métodos como o conde e indexador é uma receita para o desastre em um mundo com vários segmentos. Eu nem sequer chegado a capacidade do chamador para chamar Adicionar ou excluir em um recipiente de seu código é suposto estar no controle de.

Também retornando um tipo concreto fechamentos você em uma implementação. Hoje internamente você pode estar usando uma lista. Amanhã talvez você tornar-se multithreaded e quer usar um recipiente segmento seguro ou uma matriz ou uma fila ou a coleção valores de um dicionário ou a saída de uma consulta Linq. Se você se tranca em um tipo de retorno de concreto, em seguida, você tem que quer a mudança um monte de código ou fazer uma conversão antes de retornar.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top