Eu tenho algum código e quando ele executa, ele lança um "NullReferenceException", dizendo:
Referência do objeto não definida para uma instância de um objeto.
O que significa isto, e o que posso fazer para corrigir este erro?
Você está tentando utilizar algo que é nulo' (ou
Nada' em VB.NET). Isto significa que ou você o define como 'nulo', ou você nunca o define para nada.
Como qualquer outra coisa, nulo
é passado de mão em mão. Se é 'nulo' no método "A", pode ser que o método "B" passou um método 'nulo' a método "A".
"Nulo" pode ter significados diferentes:
null
intencionalmente para indicar que não há nenhum valor significativo disponível. Note que C# tem o conceito de datatypes nulos para variáveis (como tabelas de banco de dados podem ter campos nulos) - você pode atribuir null
a eles para indicar que não há nenhum valor armazenado nele, por exemplo int? a = null;
onde o ponto de interrogação indica que é permitido armazenar null na variável a
. Você pode verificar isso com if (a.HasValue) {...}
ou com if (a===nulo) {...}
. Variáveis nulas, como a
este exemplo, permitem acessar o valor via a.Value
explicitamente, ou tão normal quanto via a
. a.Value
lança uma InvalidOperationException
em vez de uma NullReferenceException
se a
for null
- você deve fazer a verificação antes, ou seja, se você tiver outra variável on-nullable int b;
então você deve fazer atribuições como if (a.HasValue) { b = a.Value; }
ou menor if (a != null) { b = a; }
.
O resto deste artigo entra em mais detalhes e mostra erros que muitos programadores frequentemente cometem e que podem levar a uma `NullReferenceException'.O tempo de execução lançando uma NullReferenceException
sempre significa a mesma coisa: você está tentando utilizar uma referência, e a referência não está inicializada (ou foi once inicializada, mas não está mais inicializada).
Isto significa que a referência é nula', e você não pode acessar membros (tais como métodos) através de uma referência
nula'. O caso mais simples:
string foo = null;
foo.ToUpper();
Isto irá lançar uma NullReferenceException' na segunda linha porque você não pode chamar o método da instância de
ToUpper()numa referência
string' apontando para `null'.
Como encontrar a origem de uma "NullReferenceException"? Além de olhar para a própria exceção, que será lançada exatamente no local onde ela ocorre, as regras gerais de depuração no Visual Studio se aplicam: coloque pontos de interrupção estratégicos e inspecione suas variáveis, seja passando o mouse sobre seus nomes, abrindo uma janela (Quick)Watch ou utilizando os vários painéis de depuração como Locals e Autos. Se você quiser descobrir onde a referência está ou não definida, clique com o botão direito do mouse sobre seu nome e selecione "Find All References" (Encontrar todas as referências). Você pode então colocar um ponto de parada em cada local encontrado e executar seu programa com o depurador anexado. Cada vez que o depurador quebra em tal ponto de quebra, você precisa determinar se espera que a referência não seja nula, inspecionar a variável e verificar se ela aponta para uma instância quando você espera que ela aponte. Ao seguir o fluxo do programa desta forma, você pode encontrar o local onde a instância não deve ser nula, e por que ela não está devidamente definida.
Alguns cenários comuns onde a exceção pode ser jogada:
ref1.ref2.ref3.member
Se a ref1, ref2 ou ref3 for nula, então você terá uma `NullReferenceException'. Se você quiser resolver o problema, então descubra qual deles é nulo, reescrevendo a expressão para o seu equivalente mais simples:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
Especificamente, em HttpContext.Current.User.Identity.Name', a propriedade
HttpContext.Current' pode ser nula, ou a propriedade User' pode ser nula, ou a propriedade
Identidade' pode ser nula.
public class Person {
public int Age { get; set; }
}
public class Book {
public Person Author { get; set; }
}
public class Example {
public void Foo() {
Book b1 = new Book();
int authorAge = b1.Author.Age; // You never initialized the Author property.
// there is no Person to get an Age from.
}
}
Se você quiser evitar a referência nula da criança (Pessoa), você poderia inicializá-la no construtor do objeto dos pais (Livro).
O mesmo se aplica aos inicializadores de objetos aninhados:
Book b1 = new Book { Author = { Age = 45 } };
Isso se traduz para
Book b1 = new Book();
b1.Author.Age = 45;
Enquanto a palavra-chave "nova" é utilizada, ela apenas cria uma nova instância de "Livro", mas não uma nova instância de "Pessoa", então o "Autor" a propriedade ainda é "nula".
public class Person {
public ICollection<Book> Books { get; set; }
}
public class Book {
public string Title { get; set; }
}
Os iniciadores de coleções aninhadas comportam-se da mesma maneira:
Person p1 = new Person {
Books = {
new Book { Title = "Title1" },
new Book { Title = "Title2" },
}
};
Isso se traduz para
Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });
A "nova Pessoa" apenas cria uma instância de "Pessoa", mas a coleção de "Livros" ainda é "nula". A sintaxe do inicializador da coleção não cria uma coleção
para p1.Books', ele só se traduz para as declarações
p1.Books.Add(...)`.
int[] numbers = null;
int n = numbers[0]; // numbers is null. There is no array to index.
Person[] people = new Person[5];
people[0].Age = 20 // people[0] is null. The array was allocated but not
// initialized. There is no Person to set the Age for.
long[][] array = new long[1][];
array[0][0] = 3; // is null because only the first dimension is yet initialized.
// Use array[0] = new long[2]; first.
Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
// There is no Dictionary to perform the lookup.
public class Person {
public string Name { get; set; }
}
var people = new List<Person>();
people.Add(null);
var names = from p in people select p.Name;
string firstName = names.First(); // Exception is thrown here, but actually occurs
// on the line above. "p" is null because the
// first element we added to the list is null.
public class Demo
{
public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e)
{
StateChanged(this, e); // Exception is thrown here
// if no event handlers have been attached
// to StateChanged event
}
}
Se você nomear os campos de forma diferente dos locais, você pode ter percebido que nunca inicializou o campo.
public class Form1 {
private Customer customer;
private void Form1_Load(object sender, EventArgs e) {
Customer customer = new Customer();
customer.Name = "John";
}
private void Button_Click(object sender, EventArgs e) {
MessageBox.Show(customer.Name);
}
}
Isto pode ser resolvido seguindo a convenção para prefixar campos com um sublinhado:
private Customer _customer;
public partial class Issues_Edit : System.Web.UI.Page
{
protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Only called on first load, not when button clicked
myIssue = new TestIssue();
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
myIssue.Entry = "NullReferenceException here!";
}
}
Valores da Sessão ###ASP.NET
// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();
Se a exceção ocorrer ao referenciar uma propriedade de @Model
em uma view ASP.NET MVC, você precisa entender que o Model
fica definido no seu método de ação, quando você retorna
uma view. Quando você retorna um modelo vazio (ou propriedade do modelo) do seu controller, a exceção ocorre quando as views acessam a ele:
// Controller
public class Restaurant:Controller
{
public ActionResult Search()
{
return View(); // Forgot the provide a Model here.
}
}
// Razor view
@foreach (var restaurantSearch in Model.RestaurantSearch) // Throws.
{
}
<p>@Model.somePropertyName</p> <!-- Also throws -->
Os controles WPF são criados durante a chamada para InitializarComponente' na ordem em que aparecem na árvore visual. Uma
NullReferenceException' será criada no caso de controles criados antecipadamente com manipuladores de eventos, etc. A exceção de "InitializeComponent", que faz referência aos controles criados tardiamente.
Por exemplo :
<Grid>
<!-- Combobox declared first -->
<ComboBox Name="comboBox1"
Margin="10"
SelectedIndex="0"
SelectionChanged="comboBox1_SelectionChanged">
<ComboBoxItem Content="Item 1" />
<ComboBoxItem Content="Item 2" />
<ComboBoxItem Content="Item 3" />
</ComboBox>
<!-- Label declared later -->
<Label Name="label1"
Content="Label"
Margin="10" />
</Grid>
Aqui a comboBox1
é criada antes da label1
. Se a comboBox1_SelectionChanged' tentar referenciar a
label1', ela ainda não terá sido criada.
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}
Alterar a ordem das declarações no XAML (ou seja, listando label1
antes de comboBox1
, ignorando questões de filosofia de design, resolveria pelo menos a NullReferenceException
aqui.
as
var myThing = someObject as Thing;
Isso não joga uma InvalidCastException, mas retorna um 'nulo' quando o elenco falha (e quando algum objeto é nulo por si só). Então esteja atento a isso.
As versões simples First()
e Single()
lançam exceções quando não há nada. As versões "OrDefault" retornam nulas nesse caso. Portanto, esteja ciente disso.
quando se tenta iterar a recolha nula. Normalmente causado por 'nulos' inesperados resultam de métodos que retornam coleções.
List<int> list = null;
foreach(var v in list) { } // exception
Exemplo mais realista - selecione os nós do documento XML. Irá lançar se os nós não forem encontrados, mas a depuração inicial mostra que todas as propriedades são válidas:
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
nulo
e ignorar valores nulos.Se você espera que a referência às vezes seja nula, você pode verificar se ela é `nula' antes de acessar os membros da instância:
void PrintName(Person p) {
if (p != null) {
Console.WriteLine(p.Name);
}
}
nulo
e forneça um valor padrão.Os métodos que você espera retornar uma instância podem retornar nulo
, por exemplo, quando o objeto procurado não pode ser encontrado. Você pode escolher retornar um valor padrão quando este for o caso:
string GetCategory(Book b) {
if (b == null)
return "Unknown";
return b.Category;
}
nulo
de chamadas de método e lance uma exceção personalizada.Você também pode lançar uma exceção personalizada, apenas para pegá-la no código de chamada:
string GetCategory(string bookTitle) {
var book = library.FindBook(bookTitle); // This may return null
if (book == null)
throw new BookNotFoundException(bookTitle); // Your custom exception
return book.Category;
}
Debug.Assert
se um valor nunca deve ser nulo
, para pegar o problema antes que a exceção ocorra.Quando você sabe durante o desenvolvimento que um método talvez possa, mas nunca deve retornar nulo', você pode usar
Debug.Assert()` para quebrar o mais rápido possível quando ele ocorrer:
string GetTitle(int knownBookID) {
// You know this should never return null.
var book = library.GetBook(knownBookID);
// Exception will occur on the next line instead of at the end of this method.
Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Some other code
return book.Title; // Will never throw NullReferenceException in Debug mode.
}
Embora essa verificação não acabará na sua compilação do release, fazendo com que ela jogue a NullReferenceException
novamente quando book == null
em tempo de execução no modo release.
GetValueOrDefault()
para tipos de valores nulos para fornecer um valor padrão quando eles são nulos
.DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the default value provided (DateTime.Now), because appointment is null.
appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the appointment date, not the default
??
[C#] ou If()
[VB].A abreviação para fornecer um valor padrão quando um null
é encontrado:
IService CreateService(ILogger log, Int32? frobPowerLevel)
{
var serviceImpl = new MyService(log ?? NullLog.Instance);
// Note that the above "GetValueOrDefault()" can also be rewritten to use
// the coalesce operator:
serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}
?.
ou ?[x]
para arrays (disponível em C# 6 e VB.NET 14):Isso também é às vezes chamado de operador de navegação segura ou Elvis (depois de sua forma). Se a expressão no lado esquerdo do operador for nula, então o lado direito não será avaliado, e nulo é retornado em seu lugar. Isso significa casos como este:
var title = person.Title.ToUpper();
Se a pessoa não tiver um título, isto irá lançar uma excepção porque está a tentar chamar ToUpper
a um imóvel com um valor nulo.
Em C# 5 e abaixo, isto pode ser guardado com:
var title = person.Title == null ? null : person.Title.ToUpper();
Agora a variável título será nula em vez de lançar uma exceção. C# 6 introduz uma sintaxe mais curta para isto:
var title = person.Title?.ToUpper();
Isto fará com que a variável título seja "nula", e a chamada para "ToUpper" não será feita se "person.Title" for "nula".
É claro, você até tem que verificar title
para nulo ou utilizar o operador de condição nulo junto com o operador de condição nulo (??
) para fornecer um valor padrão:
// regular null check
int titleLength = 0;
if (title != null)
titleLength = title.Length; // If title is null, this would throw NullReferenceException
// combining the `?` and the `??` operator
int titleLength = title?.Length ?? 0;
Da mesma forma, para arrays você pode utilizar ?[i]
da seguinte forma:
int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
Isto vai fazer o seguinte: Se o myIntArray for nulo, a expressão retorna nula e você pode verificá-la com segurança. Se ele contém um array, ele fará o mesmo que:
elem = myIntArray[i];
e retorna o elemento ith.
Introduzido em C# 8, os tipos de contexto nulo e referência nula realizam análises estáticas em variáveis e fornecem um aviso do compilador se um valor pode ser potencialmente nulo ou ter sido definido como nulo. Os tipos de referência nulos permitem que os tipos sejam explicitamente autorizados a serem nulos. O contexto de anotação nula e o contexto de aviso nulo podem ser definidos para um projeto usando o elemento Nullable no seu arquivo csproj. Este elemento configura como o compilador interpreta a nulidade dos tipos e que avisos são gerados. As configurações válidas são:
?
é anexado ao tipo da variável.O C# suporta "blocos iteradores" (chamados "geradores" em alguns outros idiomas populares). Exceções de desreferência nula podem ser particularmente difíceis de depurar em blocos iterator por causa da execução diferida:
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
...
FrobFactory factory = whatever;
IEnumerable<Frobs> frobs = GetFrobs();
...
foreach(Frob frob in frobs) { ... }
Se "o que quer que seja" resultar em "nulo" então "MakeFrob" irá lançar. Agora, você pode pensar que a coisa certa a fazer é isto:
// DON'T DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
if (f == null)
throw new ArgumentNullException("f", "factory must not be null");
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
Porque é que isto está errado? Porque o bloco do iterador na verdade não executou até o para cada
! A chamada para 'GetFrobs' simplesmente retorna um objeto que quando iterada irá rodar o bloco do iterador.
Ao escrever uma verificação nula como esta você evita a dereferência nula, mas você move a exceção do argumento nulo para o ponto da iteração, não para o ponto da chamada, e isso é muito confuso para debug.
A correção correta é:
// DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
// No yields in a public method that throws!
if (f == null)
throw new ArgumentNullException("f", "factory must not be null");
return GetFrobsForReal(f, count);
}
private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
{
// Yields in a private method
Debug.Assert(f != null);
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
Ou seja, fazer um método de ajuda privada que tenha a lógica de bloco de iterador, e um método de superfície pública que faça a verificação nula e devolva o iterador. Agora quando GetFrobs' é chamado, a verificação nula acontece imediatamente, e então
GetFrobsForReal' executa quando a seqüência é iterada.
Se você examinar a fonte de referência do LINQ para Objetos, você verá que esta técnica é utilizada em todo o processo. É um pouco mais difícil de escrever, mas torna a depuração de erros de nulidade muito mais fácil. Optimize seu código para a conveniência do chamador, não para a conveniência do autor.
C# tem um modo "inseguro" que é, como o nome indica, extremamente perigoso porque os mecanismos normais de segurança que fornecem segurança de memória e tipo de segurança não são aplicados. **Você não deve estar escrevendo código inseguro a menos que tenha uma compreensão completa e profunda de como a memória funciona***. No modo inseguro, você deve estar ciente de dois fatos importantes:
Isso significa que a variável em questão não é apontada para nada. Eu poderia gerar isto desta forma:
SqlConnection connection = null;
connection.Open();
Isso vai lançar o erro porque, embora eu tenha declarado a variável "conexão", ela não é apontada para nada. Quando eu tento chamar o membro de "Open", não há referência para ele resolver, e ele irá lançar o erro.
Para evitar este erro:
objeto == nulo
.A ferramenta JetBrains Resharper identificará todos os lugares no seu código que tenham a possibilidade de um erro de referência nulo, permitindo que você coloque uma verificação nula. Este erro é a fonte número um de bugs, IMHO.
Isso significa que o seu código usou uma variável de referência de objeto que foi definida como nula (ou seja, não se referia a uma instância de objeto real).
Para evitar o erro, os objetos que poderiam ser nulos devem ser testados para nulos antes de serem usados.
if (myvar != null)
{
// Go ahead and use myvar
myvar.property = ...
}
else
{
// Whoops! myvar is null and cannot be used without first
// assigning it to an instance reference
// Attempting to use myvar here will result in NullReferenceException
}