Essa é uma pergunta geral (mas estou usando C #), qual é a melhor maneira (melhor prática), você retorna uma coleção nula ou vazia para um método que tem uma coleção como um tipo de retorno?
c#
collections
Omu
fonte
fonte
Respostas:
Coleção vazia. Sempre.
Isso é péssimo:
É uma prática recomendada NUNCA retornar
null
ao retornar uma coleção ou enumerável. SEMPRE retorne um enumerável / coleção vazio. Isso evita o absurdo acima mencionado e evita que seu carro seja instigado por colegas de trabalho e usuários de suas classes.Ao falar sobre propriedades, sempre defina sua propriedade uma vez e esqueça-a
No .NET 4.6.1, você pode condensar bastante isso:
Ao falar sobre métodos que retornam enumeráveis, você pode retornar facilmente um enumerável vazio em vez de
null
...O uso
Enumerable.Empty<T>()
pode ser visto como mais eficiente do que retornar, por exemplo, uma nova coleção ou matriz vazia.fonte
IEnumerable
ouICollection
não importa tanto. De qualquer forma, se você selecionar algo do tipo,ICollection
eles também retornarãonull
... Gostaria que eles retornassem uma coleção vazia, mas os encontrei retornandonull
, então pensei em mencionar aqui. Eu diria que o padrão de uma coleção de enumerável é vazio e não nulo. Eu não sabia que era um assunto tão sensível.Nas Diretrizes de Design do Quadro, 2ª Edição (pág. 256):
Aqui está outro artigo interessante sobre os benefícios de não retornar nulos (eu estava tentando encontrar algo no blog de Brad Abram, e ele vinculou ao artigo).
Editar - como Eric Lippert comentou agora a pergunta original, eu também gostaria de vincular ao seu excelente artigo .
fonte
Depende do seu contrato e do seu caso concreto . Geralmente , é melhor retornar coleções vazias , mas às vezes ( raramente ):
null
pode significar algo mais específico;null
.Alguns exemplos concretos:
null
significaria que o elemento está ausente, enquanto uma coleção vazia renderizaria um redundante (e possivelmente incorreto)<collection />
fonte
Há um outro ponto que ainda não foi mencionado. Considere o seguinte código:
O idioma do C # retornará um enumerador vazio ao chamar esse método. Portanto, para ser consistente com o design da linguagem (e, portanto, com as expectativas do programador), uma coleção vazia deve ser retornada.
fonte
Vazio é muito mais amigável ao consumidor.
Existe um método claro de criar um enumerável vazio:
fonte
Parece-me que você deve retornar o valor que é semanticamente correto no contexto, qualquer que seja. Uma regra que diz "sempre retorne uma coleção vazia" parece um pouco simplista para mim.
Suponha que, digamos, em um sistema para um hospital, tenhamos uma função que deve retornar uma lista de todas as hospitalizações anteriores nos últimos 5 anos. Se o cliente não estiver no hospital, faz sentido retornar uma lista vazia. Mas e se o cliente deixasse essa parte do formulário de entrada em branco? Precisamos de um valor diferente para distinguir "lista vazia" de "sem resposta" ou "não sei". Poderíamos lançar uma exceção, mas não é necessariamente uma condição de erro e não necessariamente nos afasta do fluxo normal do programa.
Muitas vezes fico frustrado com sistemas que não conseguem distinguir entre zero e nenhuma resposta. Já tive várias vezes em que um sistema me pediu para digitar algum número, digito zero e recebo uma mensagem de erro informando que devo inserir um valor nesse campo. Acabei de fazer: digitei zero! Mas não aceita zero porque não pode distingui-lo de nenhuma resposta.
Resposta a Saunders:
Sim, suponho que haja uma diferença entre "A pessoa não respondeu à pergunta" e "A resposta foi zero". Esse foi o ponto do último parágrafo da minha resposta. Muitos programas são incapazes de distinguir "não sei" de branco ou zero, o que me parece uma falha potencialmente séria. Por exemplo, eu estava comprando uma casa há um ano ou mais. Eu fui a um site imobiliário e havia muitas casas listadas com um preço inicial de US $ 0. Pareceu-me muito bom: eles estão dando essas casas de graça! Mas tenho certeza que a triste realidade era que eles simplesmente não haviam entrado no preço. Nesse caso, você pode dizer: "Bem, obviamente zero significa que eles não entraram no preço - ninguém vai dar uma casa de graça". Mas o site também listou os preços médios de compra e venda de casas em várias cidades. Não posso deixar de me perguntar se a média não incluiu os zeros, fornecendo uma média incorretamente baixa para alguns lugares. ou seja, qual é a média de US $ 100.000; US $ 120.000; e "não sei"? Tecnicamente, a resposta é "não sei". O que provavelmente queremos realmente ver é de US $ 110.000. Mas provavelmente receberemos 73.333 dólares, o que seria completamente errado. Além disso, e se tivéssemos esse problema em um site onde os usuários podem fazer pedidos on-line? (Improvável no setor imobiliário, mas tenho certeza de que você já viu isso em muitos outros produtos.) Queremos realmente que "o preço ainda não especificado" seja interpretado como "gratuito"? dando assim uma média incorretamente baixa para alguns lugares. ou seja, qual é a média de US $ 100.000; US $ 120.000; e "não sei"? Tecnicamente, a resposta é "não sei". O que provavelmente queremos realmente ver é de US $ 110.000. Mas provavelmente receberemos 73.333 dólares, o que seria completamente errado. Além disso, e se tivéssemos esse problema em um site onde os usuários podem fazer pedidos on-line? (Improvável no setor imobiliário, mas tenho certeza de que você já viu isso em muitos outros produtos.) Queremos realmente que "o preço ainda não especificado" seja interpretado como "gratuito"? dando assim uma média incorretamente baixa para alguns lugares. ou seja, qual é a média de US $ 100.000; US $ 120.000; e "não sei"? Tecnicamente, a resposta é "não sei". O que provavelmente queremos realmente ver é de US $ 110.000. Mas provavelmente receberemos 73.333 dólares, o que seria completamente errado. Além disso, e se tivéssemos esse problema em um site onde os usuários podem fazer pedidos on-line? (Improvável no setor imobiliário, mas tenho certeza de que você já viu isso em muitos outros produtos.) Queremos realmente que "o preço ainda não especificado" seja interpretado como "gratuito"? o que seria completamente errado. Além disso, e se tivéssemos esse problema em um site onde os usuários podem fazer pedidos on-line? (Improvável no setor imobiliário, mas tenho certeza de que você já viu isso em muitos outros produtos.) Queremos realmente que "o preço ainda não especificado" seja interpretado como "gratuito"? o que seria completamente errado. Além disso, e se tivéssemos esse problema em um site onde os usuários podem fazer pedidos on-line? (Improvável no setor imobiliário, mas tenho certeza de que você já viu isso em muitos outros produtos.) Queremos realmente que "o preço ainda não especificado" seja interpretado como "gratuito"?
RE tendo duas funções separadas, um "existe algum?" e um "se sim, o que é?" Sim, você certamente poderia fazer isso, mas por que você gostaria? Agora, o programa de chamada deve fazer duas chamadas em vez de uma. O que acontece se um programador não chamar o "any"? e vai direto para o "o que é isso?" ? O programa retornará um zero incorreto? Lançar uma exceção? Retornar um valor indefinido? Cria mais código, mais trabalho e mais erros em potencial.
O único benefício que vejo é que ele permite que você cumpra uma regra arbitrária. Existe alguma vantagem nessa regra que vale a pena obedecer? Se não, por que se preocupar?
Resposta a Jammycakes:
Considere como seria o código real. Eu sei que a pergunta dizia C #, mas com licença se eu escrever Java. Meu C # não é muito nítido e o princípio é o mesmo.
Com um retorno nulo:
Com uma função separada:
Na verdade, é uma linha ou dois códigos a menos com retorno nulo; portanto, não é mais oneroso para o chamador, é menos.
Não vejo como isso cria um problema SECO. Não é como se tivéssemos que executar a chamada duas vezes. Se sempre quiséssemos fazer a mesma coisa quando a lista não existe, talvez pudéssemos enviar a manipulação para a função get-list em vez de fazer com que o chamador fizesse isso; portanto, colocar o código no chamador seria uma violação DRY. Mas quase certamente não queremos sempre fazer a mesma coisa. Nas funções em que precisamos ter a lista para processar, uma lista ausente é um erro que pode interromper o processamento. Mas em uma tela de edição, certamente não queremos interromper o processamento se eles ainda não inseriram dados: queremos deixá-los inserir dados. Portanto, o manuseio de "nenhuma lista" deve ser feito no nível do chamador de uma maneira ou de outra. E se fazemos isso com um retorno nulo ou uma função separada não faz diferença para o princípio maior.
Claro, se o chamador não procurar nulo, o programa poderá falhar com uma exceção de ponteiro nulo. Mas se houver uma função separada "obteve alguma" e o chamador não a chama, mas chama cegamente a função "obter lista", o que acontece? Se isso gera uma exceção ou falha, bem, é praticamente o mesmo que aconteceria se retornasse nulo e não a checasse. Se retornar uma lista vazia, isso está errado. Você não conseguiu distinguir entre "Eu tenho uma lista com zero elementos" e "Eu não tenho uma lista". É como retornar zero pelo preço quando o usuário não inseriu nenhum preço: está errado.
Não vejo como anexar um atributo adicional à coleção. O chamador ainda precisa verificar. Como isso é melhor do que procurar por nulos? Novamente, a pior coisa que pode acontecer é que o programador esqueça de verificá-lo e dê resultados incorretos.
Uma função que retorna nulo não é uma surpresa se o programador estiver familiarizado com o conceito de nulo que significa "não tem valor", que eu acho que qualquer programador competente deveria ter ouvido falar, se ele acha que é uma boa ideia ou não. Eu acho que ter uma função separada é mais um problema de "surpresa". Se um programador não estiver familiarizado com a API, quando executar um teste sem dados, descobrirá rapidamente que às vezes recebe um nulo. Mas como ele descobriria a existência de outra função, a menos que lhe ocorresse a possibilidade de tal função e ele verifique a documentação, e a documentação é completa e compreensível? Eu preferiria ter uma função que sempre me dá uma resposta significativa, em vez de duas funções que tenho que conhecer e lembrar de chamar de ambas.
fonte
Se uma coleção vazia faz sentido semanticamente, é isso que eu prefiro retornar. Retornar uma coleção vazia para
GetMessagesInMyInbox()
comunicações "você realmente não tem nenhuma mensagem na sua caixa de entrada", enquanto retornarnull
pode ser útil para comunicar que dados insuficientes estão disponíveis para dizer como deve ser a lista que pode ser retornada.fonte
null
valor certamente não parece razoável, eu estava pensando em termos mais gerais sobre esse. Exceções também são ótimas para comunicar o fato de que algo deu errado, mas se os "dados insuficientes" mencionados forem perfeitamente esperados, lançando uma exceção, o design será ruim. Estou pensando em um cenário em que é perfeitamente possível e nenhum erro para o método às vezes não conseguir calcular uma resposta.Retornar nulo pode ser mais eficiente, pois nenhum novo objeto é criado. No entanto, também costuma exigir uma
null
verificação (ou tratamento de exceção).Semanticamente,
null
e uma lista vazia não significam a mesma coisa. As diferenças são sutis e uma opção pode ser melhor que a outra em casos específicos.Independentemente da sua escolha, documente-a para evitar confusão.
fonte
Pode-se argumentar que o raciocínio por trás do Null Object Pattern é semelhante a um favor de retornar a coleção vazia.
fonte
Depende da situação. Se for um caso especial, retorne nulo. Se a função retornar uma coleção vazia, é óbvio que retornar está correto. No entanto, retornar uma coleção vazia como um caso especial por causa de parâmetros inválidos ou por outros motivos NÃO é uma boa idéia, pois está ocultando uma condição de caso especial.
Na verdade, neste caso, eu geralmente prefiro lançar uma exceção para garantir que REALMENTE não seja ignorada :)
Dizer que isso torna o código mais robusto (retornando uma coleção vazia), pois eles não precisam lidar com a condição nula é ruim, pois simplesmente oculta um problema que deve ser tratado pelo código de chamada.
fonte
Eu diria que
null
não é a mesma coisa que uma coleção vazia e você deve escolher qual deles representa melhor o que está retornando. Na maioria dos casos,null
não há nada (exceto no SQL). Uma coleção vazia é algo, embora algo vazio.Se você tiver que escolher um ou outro, eu diria que você deve tender para uma coleção vazia em vez de nula. Mas há momentos em que uma coleção vazia não é a mesma coisa que um valor nulo.
fonte
Pense sempre a favor de seus clientes (que estão usando sua API):
O retorno de 'nulo' frequentemente causa problemas com os clientes que não lidam com verificações nulas corretamente, o que causa uma NullPointerException durante o tempo de execução. Vi casos em que essa verificação nula ausente forçou um problema de produção prioritário (um cliente usado foreach (...) em um valor nulo). Durante o teste, o problema não ocorreu, porque os dados operados eram ligeiramente diferentes.
fonte
Eu gosto de explicar aqui, com exemplo adequado.
Considere um caso aqui ..
Aqui considere as funções que estou usando ..
Eu posso facilmente usar
ListCustomerAccount
e, emFindAll
vez de.,NOTA: Como o AccountValue não é
null
, a função Sum () não retornaránull
. Por isso, posso usá-lo diretamente.fonte
Tivemos essa discussão entre a equipe de desenvolvimento no trabalho há mais ou menos uma semana e, quase por unanimidade, fomos para a coleta vazia. Uma pessoa queria retornar nulo pelo mesmo motivo que Mike especificou acima.
fonte
Coleção Vazia. Se você estiver usando C #, a suposição é que maximizar os recursos do sistema não é essencial. Embora menos eficiente, o retorno da Coleção Vazia é muito mais conveniente para os programadores envolvidos (pelo motivo descrito acima).
fonte
Retornar uma coleção vazia é melhor na maioria dos casos.
O motivo disso é a conveniência da implementação do chamador, o contrato consistente e a implementação mais fácil.
Se um método retornar nulo para indicar resultado vazio, o responsável pela chamada deverá implementar um adaptador de verificação nula, além da enumeração. Esse código é duplicado em vários chamadores, por que não colocar esse adaptador dentro do método para que ele possa ser reutilizado.
Um uso válido de null para IEnumerable pode ser uma indicação de resultado ausente ou falha de operação, mas, neste caso, outras técnicas devem ser consideradas, como lançar uma exceção.
fonte
Veja aqui uma tempestade de merda elaborada
null
em geral. Não concordo com a afirmação queundefined
é outranull
, mas ainda vale a pena ler. E explica por que você deve evitarnull
e não apenas no caso solicitado. A essência é que,null
em qualquer idioma, é um caso especial. Você tem que pensarnull
como uma exceção.undefined
é diferente, pois o código que lida com o comportamento indefinido é, na maioria dos casos, apenas um bug. C e a maioria dos outros idiomas também têm comportamento indefinido, mas a maioria deles não tem identificador para isso no idioma.fonte
Da perspectiva do gerenciamento da complexidade, um objetivo principal da engenharia de software, queremos evitar a propagação de complexidade ciclomática desnecessária para os clientes de uma API. Retornar um nulo para o cliente é como retornar o custo da complexidade ciclomática de outra ramificação de código.
(Isso corresponde a uma carga de teste de unidade. Você precisaria escrever um teste para o caso de retorno nulo, além do caso de retorno de coleção vazio.)
fonte