Digamos que eu tenho um método:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
Fui treinado para acreditar que jogar o item ArgumentNullException
é "correto", mas um erro "Referência de objeto não definida para uma instância de um objeto" significa que tenho um erro.
Por quê?
Sei que, se eu estava armazenando em cache a referência someObject
e usando-a posteriormente, é melhor verificar a nulidade quando transmitida e falhar mais cedo. No entanto, se estou desreferenciando na próxima linha, por que devemos fazer a verificação? Vai lançar uma exceção de uma maneira ou de outra.
Editar :
Apenas me ocorreu ... o medo do nulo não referenciado vem de uma linguagem como C ++ que não verifica você (isto é, apenas tenta executar algum método no local de memória zero + deslocamento do método)?
Respostas:
Suponho que se você estiver desreferenciando imediatamente a variável, poderá debater de qualquer maneira, mas eu ainda preferiria a ArgumentNullException.
É muito mais explícito sobre o que está acontecendo. A exceção contém o nome da variável que era nula, enquanto uma NullReferenceException não. Particularmente no nível da API, um ArgumentNullException deixa claro que alguns chamadores não honraram o contrato de um método, e a falha não foi necessariamente um erro aleatório ou um problema mais profundo (embora esse ainda possa ser o caso). Você deve falhar cedo.
Além disso, o que os mantenedores posteriores têm mais probabilidade de fazer? Adicione ArgumentNullException se eles adicionarem código antes da primeira desreferência, ou simplesmente adicione o código e, posteriormente, permita que uma NullReferenceException seja lançada?
fonte
Suponha que eu chame o método
M(x)
que você escreveu. Eu passo nulo. Eu recebo um ArgumentNullException com o nome definido como "x". Essa exceção significa sem ambiguidade que eu tenho um bug; Eu não deveria ter passado nulo para x.Suponha que eu chame um método M que você escreveu. Eu passo nulo. Eu recebo uma exceção deref nula. Como o diabo é que eu vou saber se eu tenho um bug ou você tem um bug ou alguma biblioteca de terceiros que você ligou em meu nome tem um bug? Não sei se preciso corrigir meu código ou ligar para você para solicitar o seu.
Ambas as exceções são indicativas de erros; nenhum deles deve nunca ser lançado no código de produção. A questão é qual exceção comunica quem tem o bug melhor para a pessoa que está fazendo a depuração? A exceção nula de deref não diz quase nada; A exceção null do argumento diz muito. Seja gentil com seus usuários; lançar exceções que lhes permitam aprender com seus erros.
fonte
"neither should ever be thrown in production code"
Que outros tipos de código / situações seriam usados? Eles devem ser compilados comoDebug.Assert
? Ou você quer dizer não jogá-los fora de uma API?throw new ArgumentNullException
no seu código de produção e ele nunca deve ser executado fora de um caso de teste . A exceção nunca deve ser lançada, porque o chamador nunca deve ter esse bug.O ponto é que seu código deve fazer algo significativo.
A
NullReferenceException
não é particularmente significativo, porque pode significar tudo. Sem olhar para onde a exceção foi lançada (e o código pode não estar disponível para mim), não consigo descobrir o que deu errado.An
ArgumentNullException
é significativo, porque me diz: a operação falhou porque o argumentosomeObject
eranull
. Não preciso olhar para o seu código para descobrir isso.Ao criar essa exceção, você manipula explicitamente
null
, mesmo que sua única maneira de fazê-lo seja dizer que não pode manipulá-lo, enquanto deixar o código travar pode significar isso; você pode realmente manipular nulos. , mas esqueceu de implementar o caso.O ponto das exceções é comunicar informações em caso de falha, que podem ser tratadas no tempo de execução para se recuperar da falha ou no momento da depuração para entender melhor o que deu errado.
fonte
Acredito que a única vantagem é que você pode fornecer melhores diagnósticos na exceção. Ou seja, você sabe que o chamador da função está passando nulo, enquanto se você deixar a desreferência jogá-lo, não terá certeza se o chamador passa dados incorretos ou se há um erro na própria função.
Eu faria isso se alguém chamar a função para facilitar o uso (depurar se algo der errado) e não fazê-lo no meu código interno, porque o tempo de execução a verificará de qualquer maneira.
fonte
Você tem um erro se o código não funcionar como projetado. Um
NullReferenceException
é lançado pelo sistema e esse fato realmente torna mais um bug do que um recurso. Não apenas porque você não definiu a exceção específica a ser tratada. Também porque um desenvolvedor que lê seu código pode assumir que você não considerou a situação.Por motivos semelhantes, o que
ApplicationException
pertence ao seu aplicativo e um generalException
que pertence ao sistema operacional. O tipo de exceção que está sendo lançada indica onde a exceção se origina.Se você considerou nulo, é melhor tratá-lo normalmente, lançando uma exceção específica ou se for uma entrada aceitável; então, não lance uma exceção porque são caras. Manipule-o executando operações com bons padrões ou nenhuma operação (por exemplo, nenhuma atualização necessária).
Por fim, não negligencie a capacidade do chamador de lidar com a situação. Ao fornecer a eles uma exceção mais específica,
ArgumentException
eles podem construir sua tentativa / captura de maneira a lidar com esse erro antes do mais geral,NullReferenceException
que pode ser causado por vários outros motivos que ocorrem em outros lugares, como uma falha na inicialização de uma variável do construtor.fonte
A verificação torna mais explícito o que está acontecendo, mas o problema real vem com código como este (artificial):
Aqui há uma cadeia de chamadas envolvida e, sem verificação nula, você vê apenas o erro em someOtherObject e não onde o problema se revelou pela primeira vez - o que significa instantaneamente a perda de tempo na depuração ou interpretação das pilhas de chamadas.
Em geral, é melhor ser consistente com esse tipo de coisa e sempre adicionar a verificação nula - o que torna mais fácil identificar se está faltando um do que escolher e escolher caso a caso (supondo que você saiba que nulo é sempre inválido).
fonte
Outro motivo a considerar é se algum processamento ocorrer antes que o objeto nulo seja usado?
Digamos que você tenha um arquivo de dados de IDs de empresas associadas (Cid) e de pessoas (Pid). Ele é salvo como um fluxo de pares de números:
Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid
E essa função VB grava os registros no arquivo:
Se
Person
for uma referência nula, a linhaFile.Write(Person.ID)
lançará uma exceção. O software pode capturar a exceção e se recuperar, mas isso deixa um problema. Um ID da empresa foi gravado sem um ID da pessoa associada. De fato, quando você chamar esta função pela próxima vez, você colocará um ID da empresa onde o ID da pessoa anterior era esperado. Além de o registro estar incorreto, todos os registros subsequentes terão um ID de pessoa seguido por um ID da empresa, que é o contrário.Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid
Ao validar os parâmetros no início da função, você evita que qualquer processamento ocorra quando o método é garantido com falha.
fonte