Em C #, há bons motivos (além de uma mensagem de erro melhor) para adicionar verificações de parâmetro nulo a cada função onde nulo não é um valor válido? Obviamente, o código que usa s lançará uma exceção de qualquer maneira. E essas verificações tornam o código mais lento e difícil de manter.
void f(SomeType s)
{
if (s == null)
{
throw new ArgumentNullException("s cannot be null.");
}
// Use s
}
Respostas:
Sim, existem boas razões:
NullReferenceException
Agora, quanto às suas objeções:
E para sua afirmação:
Mesmo? Considerar:
void f(SomeType s) { // Use s Console.WriteLine("I've got a message of {0}", s); }
Isso usa
s
, mas não lança uma exceção. Se for inválido paras
ser nulo, e isso indicar que algo está errado, uma exceção é o comportamento mais apropriado aqui.Agora, onde você coloca essas verificações de validação de argumento é uma questão diferente. Você pode decidir confiar em todo o código de sua própria classe, portanto, não se preocupe com métodos privados. Você pode decidir confiar no resto de sua montagem, portanto, não se preocupe com os métodos internos. Você quase certamente deve validar os argumentos dos métodos públicos.
Uma observação lateral: a sobrecarga do construtor de parâmetro único de
ArgumentNullException
deve ser apenas o nome do parâmetro, então seu teste deve ser:if (s == null) { throw new ArgumentNullException("s"); }
Como alternativa, você pode criar um método de extensão, permitindo um pouco mais terser:
s.ThrowIfNull("s");
Na minha versão do método de extensão (genérico), faço com que ele retorne o valor original se não for nulo, permitindo que você escreva coisas como:
this.name = name.ThrowIfNull("name");
Você também pode ter uma sobrecarga que não leva o nome do parâmetro, se você não estiver muito preocupado com isso.
fonte
ThrowIfEmpty
emICollection
Debug.Assert
. É ainda mais importante detectar erros na produção (antes que eles estraguem os dados reais) do que no desenvolvimento.throw new ArgumentNullException(nameof(s))
Eu concordo com Jon, mas gostaria de acrescentar uma coisa a isso.
Minha atitude sobre quando adicionar verificações nulas explícitas baseia-se nestas premissas:
throw
declarações são declarações .if
é uma declaração .throw
emif (x == null) throw whatever;
Se não houver uma maneira possível de execução dessa instrução, ela não poderá ser testada e deverá ser substituída por
Debug.Assert(x != null);
.Se houver uma maneira possível de execução dessa instrução, escreva a instrução e um teste de unidade que a exercite.
É particularmente importante que os métodos públicos dos tipos públicos verifiquem seus argumentos dessa maneira; você não tem ideia da loucura que seus usuários vão fazer. Dê a eles o "ei seu estúpido, você está fazendo errado!" exceção o mais rápido possível.
Os métodos privados de tipos privados, por outro lado, têm muito mais probabilidade de estar na situação em que você controla os argumentos e podem ter uma forte garantia de que o argumento nunca será nulo; use uma asserção para documentar essa invariante.
fonte
Estou usando isso há um ano:
_ = s ?? throw new ArgumentNullException(nameof(s));
É um oneliner, e o descarte (
_
) significa que não há alocação desnecessária.fonte
Sem uma
if
verificação explícita , pode ser muito difícil descobrir o que eranull
se você não fosse o proprietário do código.Se você obtiver um
NullReferenceException
de dentro de uma biblioteca sem código-fonte, provavelmente terá muitos problemas para descobrir o que fez de errado.Essas
if
verificações não tornarão seu código notavelmente mais lento.Observe que o parâmetro para o
ArgumentNullException
construtor é um nome de parâmetro, não uma mensagem.Seu código deve ser
if (s == null) throw new ArgumentNullException("s");
Escrevi um snippet de código para tornar isso mais fácil:
<?xml version="1.0" encoding="utf-8" ?> <CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet"> <CodeSnippet Format="1.0.0"> <Header> <Title>Check for null arguments</Title> <Shortcut>tna</Shortcut> <Description>Code snippet for throw new ArgumentNullException</Description> <Author>SLaks</Author> <SnippetTypes> <SnippetType>Expansion</SnippetType> <SnippetType>SurroundsWith</SnippetType> </SnippetTypes> </Header> <Snippet> <Declarations> <Literal> <ID>Parameter</ID> <ToolTip>Paremeter to check for null</ToolTip> <Default>value</Default> </Literal> </Declarations> <Code Language="csharp"><![CDATA[if ($Parameter$ == null) throw new ArgumentNullException("$Parameter$"); $end$]]> </Code> </Snippet> </CodeSnippet> </CodeSnippets>
fonte
Você pode querer dar uma olhada em Contratos de código se precisar de uma maneira mais agradável de ter certeza de não obter nenhum objeto nulo como parâmetro.
fonte
O principal benefício é que você está sendo explícito com os requisitos do seu método desde o início. Isso deixa claro para outros desenvolvedores que trabalham no código que é realmente um erro um chamador enviar um valor nulo para o seu método.
A verificação também interromperá a execução do método antes que qualquer outro código seja executado. Isso significa que você não terá que se preocupar com modificações feitas pelo método que não foram concluídas.
fonte
Ele economiza alguma depuração, quando você atinge essa exceção.
O ArgumentNullException afirma explicitamente que era "s" que era nulo.
Se você não tiver essa verificação e deixar o código explodir, receberá uma NullReferenceException de alguma linha não identificada nesse método. Em uma versão de lançamento, você não obtém números de linha!
fonte
Código original:
void f(SomeType s) { if (s == null) { throw new ArgumentNullException("s cannot be null."); } // Use s }
Reescreva-o como:
void f(SomeType s) { if (s == null) throw new ArgumentNullException(nameof(s)); }
A razão para reescrever usando
nameof
é que permite uma refatoração mais fácil. Se o nome da sua variávels
mudar alguma vez, as mensagens de depuração também serão atualizadas, ao passo que, se você apenas codificar o nome da variável, ela acabará ficando desatualizada quando as atualizações forem feitas ao longo do tempo. É uma boa prática utilizada na indústria.fonte
int i = Age ?? 0;
Então, para seu exemplo:
if (age == null || age == 0)
Ou:
if (age.GetValueOrDefault(0) == 0)
Ou:
if ((age ?? 0) == 0)
Ou ternário:
int i = age.HasValue ? age.Value : 0;
fonte