Por que a “referência de objeto não definida para uma instância de um objeto” nos diz qual objeto?


39

Estamos lançando um sistema e, às vezes, recebemos a famosa exceção NullReferenceExceptioncom a mensagem Object reference not set to an instance of an object.

No entanto, em um método em que temos quase 20 objetos, ter um log que diz que um objeto é nulo é realmente inútil. É como dizer a você, quando você é o agente de segurança de um seminário, que um homem entre cem participantes é um terrorista. Isso é realmente inútil para você. Você deve obter mais informações, se quiser detectar qual homem é o homem ameaçador.

Da mesma forma, se quisermos remover o bug, precisamos saber qual objeto é nulo.

Agora, algo ficou obcecado por vários meses, e é isso:

Por que o .NET não nos fornece o nome ou, pelo menos, o tipo de referência do objeto, que é nulo? . Não consegue entender o tipo de reflexão ou qualquer outra fonte?

Além disso, quais são as melhores práticas para entender qual objeto é nulo? Devemos sempre testar a nulidade de objetos nesses contextos manualmente e registrar o resultado? Existe uma maneira melhor?

Atualização: a exceção The system cannot find the file specifiedtem a mesma natureza. Você não pode encontrar qual arquivo, até anexar ao processo e depurar. Eu acho que esses tipos de exceções podem se tornar mais inteligentes. Não seria melhor se o .NET nos dissesse em c:\temp.txt doesn't exist.vez dessa mensagem geral? Como desenvolvedor, voto sim.


14
A exceção deve incluir um rastreamento de pilha com o número da linha. Eu começaria minha investigação a partir daí, olhando para todos os objetos acessados ​​nessa linha.
PersonalNexus

2
Além disso, sempre me perguntei por que a caixa de diálogo auxiliar de exceção no Visual Studio inclui a dica "útil" newpara criar instâncias de uma classe. Quando essa dica realmente ajuda?
PersonalNexus

1
Se você tiver uma cadeia de dez chamadas de objeto, terá um problema com o acoplamento no seu design.
Pete Kirkham


9
Eu amo como todas as respostas a esta pergunta são semelhantes a "Use um depurador, registre seus erros, verifique se há nulo, a culpa é sua de qualquer maneira", que não está respondendo à pergunta, mas colocando a culpa em você. Somente no stackoverflow alguém realmente fornece uma resposta (o que eu acho que diz que é muita sobrecarga para a VM acompanhar). Mas, na verdade, as únicas pessoas que podem responder a essa pergunta adequadamente são alguém da microsoft que trabalhou no framework.
Rocklan

Respostas:


28

O NullReferenceExceptionbasicamente diz a você: você está fazendo errado. Nada mais nada menos. Não é uma ferramenta de depuração completa, pelo contrário. Nesse caso, eu diria que você está fazendo errado, porque

  • existe uma NullReferenceException
  • você não a impediu de uma maneira que você sabe por que / onde aconteceu
  • e também talvez: um método que exija 20 objetos pareça um pouco errado

Sou um grande fã de verificar tudo antes que as coisas comecem a dar errado e fornecer boas informações ao desenvolvedor. Em resumo: escreva cheques usando ArgumentNullExceptione os gostos e escreva o nome você mesmo. Aqui está uma amostra:

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

Você também pode procurar por contratos de código , ele possui peculiaridades, mas funciona muito bem e economiza algumas digitações.


17
@SaeedNeamati, você não está sugerindo que, por ter uma grande base de código, não deve fazer uma verificação decente de erros, certo? Quanto maior o projeto, mais importantes se tornam as funções ou a verificação e o relatório de erros.
stijn

6
+1 para obter bons conselhos, mesmo que não responda à pergunta real (que provavelmente apenas Anders Hejlsberg pode responder).
Ross Patterson

12
+1 para obter excelentes conselhos. @SaeedNeamati, você deve ouvir este conselho. Seu problema é causado por descuido e falta de profissionalismo no código. Se você não tem tempo para escrever código bom, você tem problemas muito maiores ...
MattDavey

3
Se você não tem tempo para escrever um bom código, definitivamente não tem tempo para escrever um código incorreto . Escrever código incorreto leva mais tempo do que escrever código válido. A menos que você realmente não se importe com bugs. E se você realmente não se importa com bugs, por que escrever o programa?
26412 MarkJ

5
@SaeedNeamati I only say that checking every object to get sure that it's not null, is not a good methodSeriously. É o melhor método. E não apenas nulo, verifique todos os argumentos em busca de valores razoáveis. Quanto mais cedo você detectar erros, mais fácil será encontrar a causa. Você não precisa voltar atrás em vários níveis no rastreamento de pilha para encontrar a chamada causadora.
jgauffin

19

Realmente deve mostrar exatamente o que você está tentando chamar. É como dizer "Há um problema. Você precisa corrigi-lo. Eu sei o que é. Eu não vou lhe contar. Você vai descobrir" É como metade das respostas neste estouro de pilha, ironicamente.

Então, como seria útil, por exemplo, se você entendesse isso ...

Object reference (HttpContext.Current) not set to instance of an object

...? Ter que entrar no código, percorrê-lo e descobrir que o que você está tentando chamar nullé bom, mas por que não nos dar uma pequena ajuda?

Concordo que geralmente é útil percorrer o código para chegar à resposta (porque você provavelmente descobrirá mais), mas muitas vezes muito tempo e frustração seriam salvos se o NullReferenceExceptiontexto fosse mais parecido com o exemplo acima.

Apenas dizendo.


Como o tempo de execução deve saber o que é nulo?
Será

3
O tempo de execução tem mais informações do que fornece. Qualquer informação útil que ela tenha, ela deve fornecer. É tudo o que estou dizendo. Em resposta à sua pergunta, por que não sabe? Se você souber a resposta, poderá fornecer um comentário melhor do que você.
LiverpoolsNumber9

Concordou, e eu diria o mesmo para KeyNotFoundExceptione muitos outros aborrecimentos ...
sinelaw

Na verdade, no caso de null dereferencing este olhares como uma limitação na forma como os dereferences CLR (graças ao comentário de Shahrooz Jefri na pergunta do OP)
sinelaw


4

Seu log deve incluir um rastreamento de pilha - que geralmente fornece uma dica sobre qual linha do método tem o problema. Pode ser necessário fazer com que sua compilação de versão inclua símbolos PDB para ter uma ideia de qual linha está o erro.

Concedido, não o ajudará neste caso:

Foo.Bar.Baz.DoSomething()

O princípio tell don't ask pode ajudar a evitar esse código.

Quanto ao motivo pelo qual as informações não estão incluídas, não tenho certeza - suspeito que pelo menos em uma compilação de depuração, se eles realmente quisessem, poderiam descobrir. Tomar um despejo de memória e abrir no WinDBG pode ajudar.


2

Exceções foram criadas como uma ferramenta para sinalizar condições não fatais excepcionais na cadeia de chamadas. Ou seja, eles não foram projetados como uma ferramenta de depuração.

Se uma exceção de ponteiro nulo fosse uma ferramenta de depuração, abortaria a execução do programa no local, permitindo que um depurador se conectasse, apontando-o diretamente na linha incriminadora. Isso daria ao programador todas as informações de contexto disponíveis. (Que é basicamente o que um Segfault devido a um acesso nulo ao ponteiro faz em C, embora um pouco grosseiro.)

No entanto, a exceção de ponteiro nulo é projetada como uma condição de tempo de execução válida que pode ser lançada e capturada no fluxo normal do programa. Consequentemente, considerações de desempenho precisam ser levadas em consideração. E qualquer personalização da mensagem de exceção requer que objetos de cadeia sejam criados, concatenados e destruídos no tempo de execução. Como tal, uma mensagem estática é indiscutivelmente mais rápida.

Não estou dizendo que o tempo de execução não pôde ser programado de uma maneira que produziria o nome da referência incriminadora. Isso poderia ser feito. Isso tornaria as exceções ainda mais lentas do que são. Se alguém se importasse o suficiente, esse recurso poderia ser alternado, para que não diminuísse o código de produção, mas permitisse uma depuração mais fácil; mas por alguma razão ninguém parece ter se importado o suficiente.


1

Acho que o cromulento acertou a cabeça, no entanto, também existe o ponto óbvio de que, se você está recebendo uma, NullReferenceExceptionpossui variáveis ​​não inicializadas. O argumento de que você tem cerca de 20 objetos sendo passados ​​para um método não pode ser considerado uma atenuação: como criador de um pedaço de código, você deve ser responsável por suas ações, que inclui sua conformidade com o restante de uma base de código, como bem como a utilização correta e correta de variáveis, etc.

é oneroso, tedioso e, às vezes, monótono, mas as recompensas no final valem a pena: muitas são as ocasiões em que tive que vasculhar arquivos de log com vários gigabytes e quase sempre são úteis. Entretanto, antes de chegar a esse estágio, o depurador pode ajudá-lo e, antes desse estágio, um bom planejamento poupará muita dor (e não quero dizer uma abordagem totalmente projetada para você codificar a solução: esboços simples e algumas anotações podem e vão seja melhor que nada).

Em relação ao Object reference not set to an instance of an objectcódigo, não podemos adivinhar os valores que podemos gostar: esse é o nosso trabalho como programadores e significa simplesmente que você passou uma variável não inicializada.


Você percebe que as pessoas costumavam oferecer justificativas como esta para escrever em linguagem assembly? E não usando coleta de lixo automática? E ser capaz de fazer aritmética - em hexadecimal? "oneroso, entediante e às vezes monótono" é como descrevemos trabalhos que devem ser automatizados.
Spike0xff 28/09/2015

0

Aprenda a usar o depurador. Este é exatamente o tipo de coisa para a qual foi projetado. Defina um ponto de interrupção no método em questão e pronto.

Basta percorrer seu código e ver exatamente quais são os valores de todas as suas variáveis ​​em determinados pontos.

Edit: Francamente, estou chocado que ninguém mais tenha mencionado o uso do depurador ainda.


2
Então você está dizendo que todas as exceções podem ser facilmente reproduzidas ao usar um depurador?
jgauffin

@jgauffin Estou dizendo que você pode usar um depurador para ver por que uma exceção está sendo lançada no código do mundo real, em vez de testes de unidade sintéticos que podem não testar totalmente o código em questão ou os próprios testes de unidade podem ter bugs nas causas eles perdem bugs no código real. Um depurador supera qualquer outra ferramenta que eu possa imaginar (exceto talvez coisas como Valgrind ou DTrace).
Cromulent

1
Então você está dizendo que sempre temos acesso ao computador com falha e que a depuração é melhor que os testes de unidade?
jgauffin

@ jgauffin Estou dizendo que se você tiver uma escolha, vá com o depurador, em vez de outras ferramentas. Obviamente, se você não tem essa escolha, é um pouco não-inicial. Claramente, este não é o caso desta pergunta, e foi por isso que dei a resposta que dei. Se a pergunta fosse: como você corrige esse problema no computador de um cliente sem nenhuma maneira de depuração remota (ou depuração local), minha resposta seria diferente. Você parece estar tentando distorcer minha resposta de maneiras que não têm relevância para a pergunta em questão.
Cromulent

0

Se você quiser ir com a resposta de @ stijn e colocar verificações nulas no seu código, esse trecho de código deve ajudar. Aqui estão algumas informações sobre trechos de código . Depois de configurá-lo, basta digitar argnull, clicar na guia duas vezes e preencher o espaço em branco.

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>

Nota: c # 6 tem agora nameofque o seu trecho poderia ser throw new ArgumentNullException(nameof($argument$))que tem as vantagens de não incluindo constantes mágicas, sendo verificada pelo compilador, e trabalhar melhor com refatoração ferramentas
Stijn
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.