Pytanie Co to jest wyjątek NullReferenceException i jak go naprawić?


Mam trochę kodu, a po jego wykonaniu wyrzuca NullReferenceException, mówiąc:

Odwołanie do obiektu nie jest ustawione na instancję obiektu.

Co to oznacza i co mogę zrobić, aby naprawić ten błąd?


1878


pochodzenie


Pomocniczy wyjątek w VS 2017 będzie bardziej pomocny w diagnozowaniu przyczyny tego wyjątku - blogs.msdn.microsoft.com/visualstudio/2016/11/28/... pod Nowy pomocnik wyjątków. - Zev Spitz
Czy możemy po prostu powiedzieć "obiekt nie został zainicjowany"? Więc jeśli masz deklarację zmiennej: SomeClass myVariable; to utworzy myVariable w odniesieniu do SomeClass, ale nie zostanie zainicjalizowane i będzie równa zeru. Musisz wywołać inicjalizację klasy, wykonując SomeClass myVariable = new SomeClass (); Lub, jeśli zwracasz odwołanie do innej zmiennej: SomeClass myVariable = anotherVariableDeclared (); - Arvin Amir
@Arvin co z `MfClass - John Saunders
Drodzy przyszli goście, odpowiedzi na to pytanie odnoszą się również do ArgumentNullException. Jeśli Twoje pytanie zostało zamknięte jako duplikat tego i masz ANE, postępuj zgodnie ze wskazówkami podanymi w odpowiedziach, aby debugować i rozwiązać problem. - Will
@Will ANE powinno się zdarzyć tylko wtedy, gdy jako parametr zostanie przekazana wartość null. Czy możesz podać przykład, jeśli pytanie ANE zostało zamknięte jako duplikat tego? - John Saunders


Odpowiedzi:


Jaka jest przyczyna?

Dolna linia

Próbujesz użyć czegoś, co jest null (lub Nothing w VB.NET). Oznacza to, że możesz to ustawić nulllub nigdy nie ustawiłeś go w ogóle.

Jak wszystko inne, null zostaje przekazany. Jeśli to jest null  w metoda "A", może to oznaczać, że metoda "B" minęła a null  do metoda "A".

null może mieć różne znaczenia:

  1. Zmienne obiektu, które są niezainicjowany i stąd wskazują na nic. W takim przypadku, jeśli uzyskasz dostęp do właściwości lub metod takich obiektów, spowoduje to NullReferenceException.
  2. Deweloper jest za pomocą null celowo wskazać, że nie ma znaczącej wartości. Zauważ, że C # ma koncepcję zerowalnych typów danych dla zmiennych (takich jak tabele bazy danych mogą mieć pola zerowalne) - możesz przypisać null do nich, aby wskazać na przykład, że nie ma w nim żadnej wartości int? a = null; gdzie znak zapytania wskazuje, że może przechowywać wartość null w zmiennej a. Możesz to sprawdzić za pomocą if (a.HasValue) {...} lub z if (a==null) {...}. Parametry zerowe, jak a w tym przykładzie zezwól na dostęp do wartości przez a.Value jawnie lub tak jak zwykle przez a.
    Uwaga dostęp do niego przez a.Value wyrzuca InvalidOperationException zamiast NullReferenceException gdyby a jest null - powinieneś zrobić to wcześniej, to znaczy, jeśli masz inną zmienną niedozwoloną int b; powinieneś robić takie zadania jak if (a.HasValue) { b = a.Value; } lub krócej if (a != null) { b = a; }.

Dalsza część tego artykułu jest bardziej szczegółowa i pokazuje błędy popełniane przez wielu programistów, które mogą prowadzić do NullReferenceException.

Dokładniej

Środowisko wykonawcze wyrzucające NullReferenceException  zawsze oznacza to samo: próbujesz użyć referencji, a referencja nie jest zainicjalizowana (lub była pewnego razu zainicjowany, ale jest już nie zainicjowany).

Oznacza to, że odniesienie jest nulli nie można uzyskać dostępu do członków (takich jak metody) przez null odniesienie. Najprostszy przypadek:

string foo = null;
foo.ToUpper();

To rzuci a NullReferenceException w drugim wierszu, ponieważ nie można wywołać metody instancji ToUpper() na string odniesienie wskazujące null.

Debugowanie

Jak znaleźć źródło NullReferenceException? Oprócz spojrzenia na sam wyjątek, który zostanie rzucony dokładnie w miejscu, w którym występuje, obowiązują ogólne zasady debugowania w Visual Studio: miejsce strategiczne punkty przerwania i sprawdź swoje zmienne, poprzez najechanie myszą na ich nazwy, otwarcie okna (Quick) Watch lub użycie różnych paneli debugowania, takich jak Locals i Autos.

Jeśli chcesz dowiedzieć się, gdzie odniesienie jest lub nie jest ustawione, kliknij jego nazwę prawym przyciskiem myszy i wybierz "Znajdź wszystkie odnośniki". Następnie możesz umieścić punkt przerwania w każdej znalezionej lokalizacji i uruchomić program z załączonym debuggerem. Za każdym razem, gdy debuger łamie taki punkt przerwania, musisz określić, czy referencja ma być wartością inną niż null, sprawdzać zmienną i sprawdzać, czy wskazuje instancję, gdy tego oczekujesz.

Wykonując w ten sposób przepływ programu, można znaleźć lokalizację, w której instancja nie powinna mieć wartości NULL i dlaczego nie jest poprawnie ustawiona.

Przykłady

Niektóre typowe scenariusze, w których można rzucić wyjątek:

Ogólny

ref1.ref2.ref3.member

Jeśli ref1 lub ref2 lub ref3 ma wartość null, otrzymasz a NullReferenceException. Jeśli chcesz rozwiązać problem, sprawdź, który z nich jest zerowy, przepisując wyrażenie na jego prostszy odpowiednik:

var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member

W szczególności w HttpContext.Current.User.Identity.Name, HttpContext.Current może mieć wartość null lub User Właściwość może mieć wartość null lub Identity Właściwość może mieć wartość NULL.

Pośredni

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.
    }
}

Jeśli chcesz uniknąć odwołania null dla elementu potomnego (Osoba), możesz zainicjować je w konstruktorze obiektu nadrzędnego (Book).

Inicjatory obiektów zagnieżdżonych

To samo dotyczy inicjatorów obiektów zagnieżdżonych:

Book b1 = new Book { Author = { Age = 45 } };

Przekłada się to na

Book b1 = new Book();
b1.Author.Age = 45;

Podczas new słowo kluczowe jest używane, tworzy tylko nowe wystąpienie Book, ale nie nowe wystąpienie Person, więc Author nieruchomość jest nadal null.

Zagnieżdżone Inicjatory Kolekcji

public class Person {
    public ICollection<Book> Books { get; set; }
}
public class Book {
    public string Title { get; set; }
}

Zagnieżdżone inicjalizatory kolekcji zachowują się tak samo:

Person p1 = new Person {
    Books = {
        new Book { Title = "Title1" },
        new Book { Title = "Title2" },
    }
};

Przekłada się to na

Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });

The new Person tworzy tylko wystąpienie Person, ale Books kolekcja jest nadal null. Składnia inicjalizatora kolekcji nie tworzy kolekcji dla p1.Books, przekłada się tylko na p1.Books.Add(...) sprawozdania.

Szyk

int[] numbers = null;
int n = numbers[0]; // numbers is null. There is no array to index.

Array Elements

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.

Jagged Arrays

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.

Kolekcja / Lista / Słownik

Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
                               // There is no Dictionary to perform the lookup.

Zmienna zakresu (pośrednia / odroczona)

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.

Wydarzenia

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
    }
}

Złe konwencje nazewnictwa:

Jeśli nazwałeś pola inaczej niż miejscowi, możesz zdać sobie sprawę, że nigdy nie zainicjowałeś tego pola.

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);
    }
}

Można to rozwiązać, postępując zgodnie z konwencją, aby poprzedzić pola znakiem podkreślenia:

private Customer _customer;

Cykl życia strony ASP.NET:

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!";
    }
}

Wartości sesji ASP.NET

// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();

ASP.NET MVC puste modele widoku

Jeśli wyjątek występuje podczas odwoływania się do właściwości @Model w widoku MVC ASP.NET, musisz zrozumieć, że Model zostaje ustawiony w twojej metodzie akcji, kiedy ty return widok. Po zwróceniu pustego modelu (lub właściwości modelu) ze sterownika wyjątek występuje, gdy widoki uzyskują do niego dostęp:

// 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 -->

Zlecenie i zdarzenia tworzenia kontroli WPF

Formanty WPF są tworzone podczas wywołania InitializeComponent w kolejności, w jakiej pojawiają się w drzewie wizualnym. ZA NullReferenceException zostanie podniesiony w przypadku wczesnych mechanizmów kontrolnych z modułami obsługi zdarzeń itp., które będą uruchamiane podczas InitializeComponent które odwołują się do późno utworzonych kontroli.

Na przykład :

<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>

Tutaj comboBox1 jest tworzony wcześniej label1. Gdyby comboBox1_SelectionChanged próby odwołania się do `etykieta1, nie zostanie jeszcze utworzona.

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}

Zmiana kolejności deklaracji w XAML (tzn label1 przed comboBox1, ignorując kwestie filozofii projektowania, przynajmniej rozwiąże problem NullReferenceException tutaj.

Rzuć za pomocą as

var myThing = someObject as Thing;

Nie powoduje to wystąpienia wyjątku InvalidCastException, ale zwraca wartość a null gdy rzutowanie się nie powiedzie (i gdy someObject samo jest puste). Bądź tego świadomy.

LINQ FirstOrDefault () i SingleOrDefault ()

Proste wersje First() i Single() rzucać wyjątki, gdy nic nie ma. Wersje "OrDefault" zwracają w tym przypadku wartość null. Bądź tego świadomy.

dla każdego

foreach wyrzuca przy próbie iterowania kolekcji zerowej. Zwykle spowodowane przez nieoczekiwane null wynikają z metod, które zwracają kolekcje.

 List<int> list = null;    
 foreach(var v in list) { } // exception

Bardziej realistyczny przykład - wybierz węzły z dokumentu XML. Rzucę jeśli węzły nie zostaną znalezione, ale wstępne debugowanie pokazuje, że wszystkie właściwości są prawidłowe:

 foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))

Sposoby, których należy unikać

Sprawdź jawnie null i ignoruj ​​wartości puste.

Jeśli spodziewasz się, że odwołanie czasami będzie miało wartość NULL, możesz to sprawdzić null przed uzyskaniem dostępu do członków instancji:

void PrintName(Person p) {
    if (p != null) {
        Console.WriteLine(p.Name);
    }
}

Sprawdź jawnie null i podaj wartość domyślną.

Wywołania metod, które oczekują na zwrócenie, może zwrócić instancja null, na przykład, gdy poszukiwany przedmiot nie może zostać znaleziony. W takim przypadku możesz zwrócić wartość domyślną:

string GetCategory(Book b) {
    if (b == null)
        return "Unknown";
    return b.Category;
}

Sprawdź jawnie null od wywołań metod i wyrzuć niestandardowy wyjątek.

Możesz także rzucić niestandardowy wyjątek, aby go złapać w kodzie wywołującym:

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;
}

Posługiwać się Debug.Assert jeśli wartość nigdy nie powinna być null, aby złapać problem wcześniej niż wystąpi wyjątek.

Kiedy wiesz, że w trakcie rozwoju metoda może, ale nigdy nie powinna powrócić null, możesz użyć Debug.Assert() przerwać jak najszybciej, kiedy to nastąpi:

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.
}

Chociaż ten czek nie skończy się twoją wersją wydania, powodując, że rzuci NullReferenceException znowu kiedy book == null w czasie wykonywania w trybie zwolnienia.

Posługiwać się GetValueOrDefault() dla typów wartości zerowych, aby zapewnić domyślną wartość, gdy są null.

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

Użyj operatora koalescencyjnego zerowego: ?? [C #] lub If() [VB].

Skrót do podania wartości domyślnej, gdy null napotkano:

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;
}

Użyj operatora warunku pustego: ?. lub ?[x] dla tablic (dostępne w C # 6 i VB.NET 14):

Jest to czasami nazywane bezpieczną nawigacją lub operatorem Elvisa (po jego kształcie). Jeśli wyrażenie po lewej stronie operatora ma wartość null, wówczas prawa strona nie zostanie oceniona, a zamiast tego zostanie zwrócona wartość null. Oznacza to takie przypadki:

var title = person.Title.ToUpper();

Jeśli dana osoba nie ma tytułu, spowoduje to zgłoszenie wyjątku, ponieważ próbuje nawiązać połączenie ToUpper na właściwości o wartości pustej.

W C # 5 i poniżej, to może być strzeżone przez:

var title = person.Title == null ? null : person.Title.ToUpper();

Teraz zmienna tytułu będzie null zamiast rzucać wyjątek. C # 6 wprowadza krótszą składnię do tego:

var title = person.Title?.ToUpper();

Spowoduje to powstanie zmiennej tytułu nulli połączenie z ToUpper nie jest, jeśli person.Title jest null.

Oczywiście ty nadal musieć sprawdzić title dla wartości null lub użyj operatora warunku pustego razem z operatorem koalescencyjnym o wartości null (??), aby podać wartość domyślną:

// 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;

Podobnie, w przypadku tablic, których możesz użyć ?[i] następująco:

int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");

Spowoduje to wykonanie następujących czynności: Jeśli właściwość myIntArray ma wartość null, wyrażenie zwróci wartość null i można ją bezpiecznie sprawdzić. Jeśli zawiera tablicę, zrobi to tak samo, jak: elem = myIntArray[i]; i zwraca ith element.

Specjalne techniki debugowania i naprawiania zerowych derefów w iteratorach

C # obsługuje "bloki iteracyjne" (zwane "generatorami" w niektórych innych popularnych językach). Wyjątki z wyjątkiem dereferencji mogą być szczególnie trudne do debugowania w blokach iteracyjnych z powodu opóźnionego wykonania:

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) { ... }

Gdyby whatever prowadzi do null następnie MakeFrob rzuci. Teraz możesz pomyśleć, że właściwą rzeczą jest:

// 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();
}

Dlaczego to jest złe? Ponieważ blok iteratora w rzeczywistości nie biegać dopóki foreach! Wezwanie do GetFrobs po prostu zwraca obiekt, który podczas iteracji uruchomi blok iteratora.

Pisząc taką zerową kontrolę, zapobiegniesz dereferencji zerowej, ale przeniesiesz wyjątek argumentu zerowego do punktu iteracja, nie do punktu połączenie, i to jest bardzo mylące do debugowania.

Poprawna poprawka to:

// 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();
}

Oznacza to, że należy utworzyć prywatną metodę pomocniczą, która ma logikę bloku iteratora, oraz metodę powierzchni publicznej, która sprawdza wartość zerową i zwraca iterator. Teraz kiedy GetFrobs jest wywoływana, kontrola zerowa dzieje się natychmiast, a następnie GetFrobsForReal wykonuje się, gdy sekwencja jest iterowana.

Jeśli przeanalizujesz źródło odniesienia dla LINQ do Objects, zobaczysz, że ta technika jest używana w całym tekście. Jest nieco bardziej niezgrabny w pisaniu, ale znacznie ułatwia debugowanie błędów dotyczących nieważności. Zoptymalizuj kod dla wygody rozmówcy, a nie dla wygody autora.

Uwaga dotycząca dereferencji zerowych w niebezpiecznym kodzie

C # ma "niebezpieczny" tryb, który, jak sama nazwa wskazuje, jest bardzo niebezpieczny, ponieważ normalne mechanizmy bezpieczeństwa, które zapewniają bezpieczeństwo pamięci i bezpieczeństwo typu, nie są egzekwowane. Nie powinieneś pisać niezabezpieczonego kodu, chyba że masz gruntowną wiedzę na temat działania pamięci.

W trybie niebezpiecznym należy pamiętać o dwóch ważnych faktach:

  • wyłuskiwanie wartości zerowej wskaźnik generuje taki sam wyjątek, jak dereferencja wartości zerowej odniesienie
  • wyłuskiwanie niepoprawnego wskaźnika niepustego mogą wytworzyć ten wyjątek w niektórych okolicznościach

Aby zrozumieć, dlaczego tak jest, pomaga zrozumieć, w jaki sposób .NET generuje wyjątki od dereferencji zerowej. (Te szczegóły dotyczą usługi .NET działającej w systemie Windows, inne systemy operacyjne używają podobnych mechanizmów).

Pamięć jest wirtualizowana w systemie Windows; każdy proces otrzymuje wirtualną przestrzeń pamięci wielu "stron" pamięci, które są śledzone przez system operacyjny. Każda strona pamięci zawiera ustawione flagi, które określają sposób jej użycia: odczyt, zapis, wykonanie i tak dalej. The najniższy strona jest oznaczona jako "produkuj błąd, jeśli kiedykolwiek był używany w jakikolwiek sposób".

Zarówno wskaźnik zerowy, jak i odwołanie zerowe w języku C # są wewnętrznie reprezentowane jako liczba zero, a więc każda próba dereferencji do odpowiedniego miejsca w pamięci powoduje, że system operacyjny generuje błąd. Środowisko wykonawcze .NET wykrywa ten błąd i przekształca go w wyjątek dereferencji zerowej.

Dlatego wyłuskiwanie zarówno zerowego wskaźnika, jak i zerowego odwołania powoduje ten sam wyjątek.

A co z drugim punktem? Dereferencje każdy nieprawidłowy wskaźnik, który znajduje się na najniższej stronie pamięci wirtualnej, powoduje ten sam błąd systemu operacyjnego i tym samym wyjątek.

Dlaczego to ma sens? Załóżmy, że mamy strukturę zawierającą dwa pliki typu int i wskaźnik niezarządzany równy zeru. Jeśli spróbujemy wyłuskać drugą int w strukturze, CLR nie będzie próbował uzyskać dostępu do pamięci w miejscu zero; uzyska dostęp do magazynu w miejscu czwartym. Ale logicznie jest to dereferencja zerowa, ponieważ docieramy do tego adresu przez wartość pusta.

Jeśli pracujesz z niebezpiecznym kodem i otrzymujesz wyjątek o dereferencji zerowej, po prostu pamiętaj, że wskaźnik zakłócający nie musi mieć wartości NULL. Może to być dowolna lokalizacja na najniższej stronie, a ten wyjątek zostanie wygenerowany.


2111



Może to jest głupi komentarz, ale czy pierwszym i najlepszym sposobem uniknięcia tego problemu byłaby inicjalizacja obiektu? Dla mnie, jeśli ten błąd występuje, zwykle jest tak, ponieważ zapomniałem zainicjować coś takiego jak element tablicy. Myślę, że znacznie mniej powszechne jest definiowanie obiektu jako zerowego, a następnie odwoływanie się do niego. Może podać sposób rozwiązania każdego problemu sąsiadującego z opisem. Nadal dobry post. - JPK
Co jeśli nie ma obiektu, a raczej wartość zwracana przez metodę lub właściwość? - John Saunders
Przykład książki / autora jest trochę dziwny ... Jak to się kompiluje? Jak działa intellisense? Co to jest? Nie jestem dobry z komputerem ... - Will
@Will: czy moja ostatnia pomoc w edycji? Jeśli nie, prosimy bardziej szczegółowo o tym, co widzisz jako problem. - John Saunders
@JohnSaunders Och, nie, przepraszam, miałem na myśli obiekt inicjalizujący wersję tego. new Book { Author = { Age = 45 } }; W jaki sposób inicjalizacja wewnętrzna nawet ... Nie mogę wymyślić sytuacji, w której wewnętrzny init mógłby kiedykolwiek działać, ale kompiluje się i działa intellisense ... O ile nie chodzi o struktury? - Will


Wyjątek NullReference - Visual Basic

The NullReference Exception dla Visual Basic nie różni się od tego w DO#. W końcu oboje zgłaszają ten sam wyjątek zdefiniowany w .NET Framework, z którego korzystają. Przyczyny wyjątkowe dla Visual Basic są rzadkie (być może tylko jedno).

W tej odpowiedzi użyjesz terminów, składni i kontekstu języka Visual Basic. Użyte przykłady pochodzą z wielu wcześniejszych pytań związanych z przepełnieniem stosu. Ma to na celu maksymalizację trafności przy użyciu rodzaje sytuacji często spotykanych w postach. Dla tych, którzy mogą jej potrzebować, dostępne jest nieco więcej wyjaśnień. Przykład podobny do twojego jest bardzo prawdopodobnie wymienione tutaj.

Uwaga:

  1. Jest to oparte na koncepcji: nie ma kodu, który można wkleić do projektu. Ma to pomóc ci zrozumieć, co powoduje a NullReferenceException (NRE), jak go znaleźć, jak go naprawić i jak go uniknąć. NRE może być spowodowany na wiele sposobów, więc nie jest to jedyne spotkanie.
  2. Przykłady (z Postów przepełnienia stosu) nie zawsze pokazują najlepszy sposób na zrobienie czegoś na pierwszym miejscu.
  3. Zazwyczaj stosuje się najprostszy środek zaradczy.

Podstawowe znaczenie

Wiadomość "Obiekt nie ustawiony na instancję obiektu" oznacza, że ​​próbujesz użyć obiektu, który nie został zainicjalizowany. Sprowadza się to do jednego z poniższych:

  • Twój kod zdeklarowany zmienna obiektowa, ale tak się nie stało zainicjować to (utwórz instancję lub "utworzyć instancję' to)
  • Coś, co twój kod założył, zainicjował obiekt, nie zrobił tego
  • Być może inny kod przedwcześnie unieważnił obiekt, który jest w użyciu

Znalezienie przyczyny

Ponieważ problem jest odniesienie do obiektu, który jest Nothing, odpowiedź polega na zbadaniu ich, aby dowiedzieć się, który z nich. Następnie określ, dlaczego nie został zainicjowany. Przytrzymaj mysz nad różnymi zmiennymi, a Visual Studio (VS) pokaże ich wartości - sprawcą będzie Nothing.

IDE debug display

Powinieneś także usunąć wszystkie bloki Try / Catch z odpowiedniego kodu, szczególnie te, w których nie ma nic w bloku Catch. Spowoduje to awarię twojego kodu, gdy spróbuje użyć obiektu, który jest Nothing. To jest to, czego chcesz ponieważ określi dokładną Lokalizacja problemu i pozwala zidentyfikować obiekt, który go powoduje.

ZA MsgBox w oknie Catch wyświetlającym Error while... będzie mało pomocne. Ta metoda również prowadzi do bardzo źle Pytania dotyczące przepełnienia stosu, ponieważ nie można opisać rzeczywistego wyjątku, obiektu, a nawet linii kodu, w którym ma miejsce.

Możesz także użyć Locals Window (Debuguj -> Windows -> Locals), aby zbadać twoje przedmioty.

Kiedy już wiesz, co i gdzie jest problem, zwykle jest to dość łatwe do naprawienia i szybsze niż publikowanie nowego pytania.

Zobacz też:

Przykłady i środki zaradcze

Obiekty klasowe / Tworzenie instancji

Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE

Problemem jest Dim nie tworzy kasy CashRejestr obiekt; deklaruje tylko zmienną o nazwie reg tego typu. Deklaracja zmienna obiektu i tworzenie instancja są dwie różne rzeczy.

Zaradzić

The New Operator może być często użyty do utworzenia instancji, gdy ją zadeklarujesz:

Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister

Gdy jest tylko właściwe utworzenie instancji później:

Private reg As CashRegister         ' Declare
  ...
reg = New CashRegister()            ' Create instance

Uwaga: Nie rób posługiwać się Dim ponownie w procedurze, w tym w konstruktorze (Sub New):

Private reg As CashRegister
'...

Public Sub New()
   '...
   Dim reg As New CashRegister
End Sub

Spowoduje to utworzenie lokalny zmienna, reg, który istnieje tylko w tym kontekście (pod). The reg zmienna z poziomem modułu Scope którego użyjesz wszędzie, gdzie pozostaniesz Nothing.

Brakuje New operator jest przyczyną nr 1 NullReference Exceptions widoczne w recenzowanych pytaniach dotyczących przepełnienia stosu.

Visual Basic stara się, aby proces był wielokrotnie powtarzany za pomocą New: Używając New Operator tworzy Nowy obiekt i połączenia Sub New - Konstruktor - gdzie twój obiekt może wykonać dowolną inną inicjalizację.

Aby było jasne, Dim (lub Private) tylko deklaruje zmienna i jej Type. The Zakres Zmienna - czy istnieje dla całego modułu / klasy, czy jest lokalna dla procedury - jest określona przez gdzie jest zadeklarowany. Private | Friend | Public określa poziom dostępu, a nie Zakres.

Aby uzyskać więcej informacji, zobacz:


Tablice

Tablice należy również utworzyć instancję:

Private arr as String()

Ta tablica została zadeklarowana, a nie utworzona. Istnieje kilka sposobów na zainicjowanie tablicy:

Private arr as String() = New String(10){}
' or
Private arr() As String = New String(10){}

' For a local array (in a procedure) and using 'Option Infer':
Dim arr = New String(10) {}

Uwaga: Począwszy od VS 2010, podczas inicjowania lokalnej tablicy za pomocą literału i Option Infer, As <Type> i New elementy są opcjonalne:

Dim myDbl As Double() = {1.5, 2, 9.9, 18, 3.14}
Dim myDbl = New Double() {1.5, 2, 9.9, 18, 3.14}
Dim myDbl() = {1.5, 2, 9.9, 18, 3.14}

Typ danych i rozmiar macierzy są określane na podstawie przypisywanych danych. Deklaracje poziomu klasy / modułu nadal wymagają As <Type> z Option Strict:

Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}

Przykład: Tablica obiektów klasy

Dim arrFoo(5) As Foo

For i As Integer = 0 To arrFoo.Count - 1
   arrFoo(i).Bar = i * 10       ' Exception
Next

Tablica została utworzona, ale Fooobiekty w nim nie mają.

Zaradzić

For i As Integer = 0 To arrFoo.Count - 1
    arrFoo(i) = New Foo()         ' Create Foo instance
    arrFoo(i).Bar = i * 10
Next

Używać List(Of T) sprawi, że trudno będzie mieć element bez poprawnego obiektu:

Dim FooList As New List(Of Foo)     ' List created, but it is empty
Dim f As Foo                        ' Temporary variable for the loop

For i As Integer = 0 To 5
    f = New Foo()                    ' Foo instance created
    f.Bar =  i * 10
    FooList.Add(f)                   ' Foo object added to list
Next

Aby uzyskać więcej informacji, zobacz:


Listy i kolekcje

Kolekcje .NET (z których istnieje wiele odmian - Listy, Słownik itp.) Również muszą być tworzone lub tworzone.

Private myList As List(Of String)
..
myList.Add("ziggy")           ' NullReference

Ten sam wyjątek występuje z tego samego powodu - myList został zadeklarowany, ale nie utworzono żadnej instancji. Rozwiązanie jest takie samo:

myList = New List(Of String)

' Or create an instance when declared:
Private myList As New List(Of String)

Powszechnym niedopatrzeniem jest klasa, która korzysta z kolekcji Type:

Public Class Foo
    Private barList As List(Of Bar)

    Friend Function BarCount As Integer
        Return barList.Count
    End Function

    Friend Sub AddItem(newBar As Bar)
        If barList.Contains(newBar) = False Then
            barList.Add(newBar)
        End If
    End Function

Każda procedura spowoduje NRE, ponieważ barList jest tylko zadeklarowane, nie utworzone. Tworzenie instancji Foo nie utworzy również instancji wewnętrznej barList. Być może było to zamierzone w konstruktorze:

Public Sub New         ' Constructor
    ' Stuff to do when a new Foo is created...
    barList = New List(Of Bar)
End Sub

Tak jak poprzednio, jest to nieprawidłowe:

Public Sub New()
    ' Creates another barList local to this procedure
     Dim barList As New List(Of Bar)
End Sub

Aby uzyskać więcej informacji, zobacz List(Of T) Klasa.


Obiekty dostawców danych

Praca z bazami danych daje wiele możliwości dla NullReference, ponieważ może istnieć wiele obiektów (Command, Connection, Transaction, Dataset, DataTable, DataRows....) w użyciu na raz. Uwaga: Nie ma znaczenia, jakiego dostawcy danych używasz - MySQL, SQL Server, OleDB itd. - koncepcje są takie same.

Przykład 1

Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim MaxRows As Integer

con.Open()
Dim sql = "SELECT * FROM tblfoobar_List"
da = New OleDbDataAdapter(sql, con)
da.Fill(ds, "foobar")
con.Close()

MaxRows = ds.Tables("foobar").Rows.Count      ' Error

Tak jak poprzednio ds Obiekt zestawu danych został zadeklarowany, ale instancja nigdy nie została utworzona. The DataAdapter wypełni istniejący DataSetnie twórz jednego. W tym przypadku od ds jest zmienną lokalną, IDE ostrzega cię że może się to zdarzyć:

img

Po zadeklarowaniu jako zmienna poziomu modułu / klasy, tak jak się wydaje con, kompilator nie może wiedzieć, czy obiekt został utworzony za pomocą procedury wstępnej. Nie ignoruj ​​ostrzeżeń.

Zaradzić

Dim ds As New DataSet

Przykład 2

ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Employees")

txtID.Text = ds.Tables("Employee").Rows(0).Item(1)
txtID.Name = ds.Tables("Employee").Rows(0).Item(2)

Problem stanowi literówka: Employees vs Employee. Nie było DataTable nazwano "Pracownik", więc a NullReferenceException wyniki próbujące uzyskać do niego dostęp. Kolejny potencjalny problem zakłada, że ​​będzie Items co może nie być, gdy SQL zawiera klauzulę WHERE.

Zaradzić

Ponieważ używa jednej tabeli, używając Tables(0) uniknie błędów pisowni. Badanie Rows.Count może również pomóc:

If ds.Tables(0).Rows.Count > 0 Then
    txtID.Text = ds.Tables(0).Rows(0).Item(1)
    txtID.Name = ds.Tables(0).Rows(0).Item(2)
End If

Fill jest funkcją zwracającą liczbę Rows dotyczy, które mogą być testowane:

If da.Fill(ds, "Employees") > 0 Then...

Przykład 3

Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,
        TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOIN
        FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)
Dim ds As New DataSet
da.Fill(ds)

If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then

The DataAdapter zapewni TableNames jak pokazano w poprzednim przykładzie, ale nie parsuje nazw z tabeli SQL lub bazy danych. W rezultacie, ds.Tables("TICKET_RESERVATION") odwołuje się do nieistniejącej tabeli.

The Zaradzić jest taki sam, odwołaj się do tabeli według indeksu:

If ds.Tables(0).Rows.Count > 0 Then

Zobacz też Klasa DataTable.


Ścieżki obiektów / zagnieżdżone

If myFoo.Bar.Items IsNot Nothing Then
   ...

Kod tylko testuje Items podczas gdy jedno i drugie myFoo i Bar może też być Nic. The zaradzić polega na przetestowaniu całego łańcucha lub ścieżki obiektów po jednym na raz:

If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....

AndAlso jest ważne. Kolejne testy nie będą wykonywane po pierwszym False warunek napotkany. Dzięki temu kod może bezpiecznie "wiercić" do obiektu (ów) jeden "poziom" na raz, oceniając myFoo.Bar tylko po (i jeśli) myFoo jest określony jako ważny. Łańcuchy obiektów lub ścieżki mogą stać się dość długie podczas kodowania złożonych obiektów:

myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")

Nie można odwoływać się do niczego "w dół" a null obiekt. Dotyczy to również kontroli:

myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"

Tutaj, myWebBrowser lub Document może być Nic lub formfld1 element może nie istnieć.


Kontrolki interfejsu użytkownika

Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar " _
     & "FROM Invoice where invoice_no = '" & _
     Me.ComboBox5.SelectedItem.ToString.Trim & "' And category = '" & _
     Me.ListBox1.SelectedItem.ToString.Trim & "' And item_name = '" & _
     Me.ComboBox2.SelectedValue.ToString.Trim & "' And expiry_date = '" & _
     Me.expiry.Text & "'", con)

Kod ten nie przewiduje między innymi, że użytkownik nie wybrał czegoś w co najmniej jednej kontrolce interfejsu użytkownika. ListBox1.SelectedItem może być Nothing, więc ListBox1.SelectedItem.ToString spowoduje NRE.

Zaradzić

Sprawdź poprawność danych przed użyciem (również użyj Option Strict i parametry SQL):

Dim expiry As DateTime         ' for text date validation
If (ComboBox5.SelectedItems.Count > 0) AndAlso
    (ListBox1.SelectedItems.Count > 0) AndAlso
    (ComboBox2.SelectedItems.Count > 0) AndAlso
    (DateTime.TryParse(expiry.Text, expiry) Then

    '... do stuff
Else
    MessageBox.Show(...error message...)
End If

Alternatywnie możesz użyć (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Visual Basic Forms

Public Class Form1

    Private NameBoxes = New TextBox(5) {Controls("TextBox1"), _
                   Controls("TextBox2"), Controls("TextBox3"), _
                   Controls("TextBox4"), Controls("TextBox5"), _
                   Controls("TextBox6")}

    ' same thing in a different format:
    Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}

    ' Immediate NRE:
    Private somevar As String = Me.Controls("TextBox1").Text

Jest to dość powszechny sposób na uzyskanie NRE. W języku C #, w zależności od tego, jak jest kodowany, IDE to zgłosi Controls nie istnieje w bieżącym kontekście lub "nie może odwoływać się do elementu niestatycznego". Tak więc, w pewnym stopniu, jest to sytuacja wyłącznie VB. Jest to również złożone, ponieważ może spowodować kaskadę awarii.

Tablic i kolekcji nie można zainicjować w ten sposób. Ten kod inicjujący zostanie uruchomiony przed konstruktor tworzy Form albo Controls. W rezultacie:

  • Listy i kolekcja będą po prostu puste
  • Tablica będzie zawierała pięć elementów Nic
  • The somevar przypisanie spowoduje natychmiastowe NRE, ponieważ nic nie ma .Text własność

Odwoływanie się do elementów tablicy później spowoduje NRE. Jeśli to zrobisz Form_Load, z powodu dziwnego błędu, IDE może nie zgłoś wyjątek, gdy się to stanie. Wyskoczy wyjątek później kiedy twój kod spróbuje użyć tablicy. Ten "cichy wyjątek" jest szczegółowe w tym poście. Dla naszych celów kluczem jest to, że gdy powstanie coś katastroficznego podczas tworzenia formularza (Sub New lub Form Load zdarzenie), wyjątki mogą nie być zgłaszane, kod kończy procedurę i wyświetla tylko formularz.

Ponieważ żaden inny kod w twoim Sub New lub Form Load wydarzenie odbędzie się po NRE, wiele innych rzeczy można pozostawić niezainicjowany.

Sub Form_Load(..._
   '...
   Dim name As String = NameBoxes(2).Text        ' NRE
   ' ...
   ' More code (which will likely not be executed)
   ' ...
End Sub

Uwaga odnosi się to do wszelkich kontroli i referencji komponentów, czyniąc je nielegalnymi tam, gdzie są:

Public Class Form1

    Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
    Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
    Private studentName As String = TextBox13.Text

Częściowy remedium

Ciekawe, że VB nie dostarcza ostrzeżenia, ale środek zaradczy to ogłosić pojemniki na poziomie formularza, ale zainicjować ich w obsłudze zdarzenia ładowania formularza podczas kontroli zrobić istnieć. Można to zrobić w Sub New tak długo jak twój kod jest po InitializeComponent połączenie:

' Module level declaration
Private NameBoxes as TextBox()
Private studentName As String

' Form Load, Form Shown or Sub New:
'
' Using the OP's approach (illegal using OPTION STRICT)
NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
studentName = TextBox32.Text           ' For simple control references

Kod tablicy może jeszcze nie być z lasu. Wszelkie kontrole, które znajdują się w kontrolce kontenera (np GroupBox lub Panel) nie zostaną znalezione w Me.Controls; będą w kolekcji Controls tego panelu lub GroupBox. Kontrola nie zostanie zwrócona, gdy nazwa kontrolna jest błędna ("TeStBox2"). W takich sprawach, Nothing ponownie będzie przechowywany w tych elementach tablicy, a wynik NRE pojawi się, gdy spróbujesz odwołać się do niego.

Te powinny być łatwe do znalezienia teraz, gdy wiesz, czego szukasz: VS shows you the error of your ways

"Button2" znajduje się na Panel

Zaradzić

Zamiast pośrednich referencji z imienia i nazwiska za pomocą formularza Controlskolekcja, użyj odniesienia do kontroli:

' Declaration
Private NameBoxes As TextBox()

' Initialization -  simple and easy to read, hard to botch:
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)

' Initialize a List
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' or
NamesList = New List(Of TextBox)
NamesList.AddRange({TextBox1, TextBox2, TextBox3...})

Funkcja Przywracanie Nic

Private bars As New List(Of Bars)        ' Declared and created

Public Function BarList() As List(Of Bars)
    bars.Clear
    If someCondition Then
        For n As Integer = 0 to someValue
            bars.Add(GetBar(n))
        Next n
    Else
        Exit Function
    End If

    Return bars
End Function

Jest to przypadek, w którym IDE ostrzeże Cię, że "nie wszystkie ścieżki zwracają wartość i NullReferenceException może spowodować". Możesz wyłączyć ostrzeżenie, wymieniając Exit Function z Return Nothing, ale to nie rozwiązuje problemu. Wszystko, co próbuje użyć zwrotu, kiedy someCondition = False spowoduje NRE:

bList = myFoo.BarList()
For Each b As Bar in bList      ' EXCEPTION
      ...

Zaradzić

Zastąpić Exit Function w funkcji z Return bList. Zwracam pusty  List to nie to samo, co powrót Nothing. Jeśli istnieje szansa, że ​​zwracany obiekt może być Nothing, sprawdź przed użyciem:

 bList = myFoo.BarList()
 If bList IsNot Nothing Then...

Słabo wdrożony try / catch

Źle zaimplementowana funkcja Try / Catch może ukryć problem i spowodować powstanie nowych:

Dim dr As SqlDataReader
Try
    Dim lnk As LinkButton = TryCast(sender, LinkButton)
    Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
    Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
    ViewState("username") = eid
    sqlQry = "select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
             Pager, mailaddress, from employees1 where username='" & eid & "'"
    If connection.State <> ConnectionState.Open Then
        connection.Open()
    End If
    command = New SqlCommand(sqlQry, connection)

    'More code fooing and barring

    dr = command.ExecuteReader()
    If dr.Read() Then
        lblFirstName.Text = Convert.ToString(dr("FirstName"))
        ...
    End If
    mpe.Show()
Catch

Finally
    command.Dispose()
    dr.Close()             ' <-- NRE
    connection.Close()
End Try

Jest to przypadek, gdy obiekt nie jest tworzony zgodnie z oczekiwaniami, ale także demonstruje przeciwwagę przydatności pustego Catch.

W SQL jest dodatkowy przecinek (po 'mailaddress'), który powoduje wyjątek na .ExecuteReader. Po Catch nic nie robi, Finally próbuje wykonać porządki, ale ponieważ nie możesz Close a null DataReader obiekt, zupełnie nowy NullReferenceException wyniki.

Pusty Catch blok to plac zabaw diabła. Ten OP był zaskoczony, dlaczego dostał NRE w Finally blok. W innych sytuacjach pusta Catch może spowodować, że coś znacznie bardziej w dół stanie się przyczyną i sprawi, że poświęcisz czas na szukanie niewłaściwych rzeczy w niewłaściwym miejscu dla problemu. (Opisany powyżej "cichy wyjątek" zapewnia tę samą wartość rozrywkową).

Zaradzić

Nie używaj pustych bloków Try / Catch - pozwól na awarię kodu, dzięki czemu możesz a) zidentyfikować przyczynę b) określić lokalizację i c) zastosować odpowiednie rozwiązanie. Try / Catchblocks nie są przeznaczone do ukrywania wyjątków od osoby posiadającej wyjątkowe kwalifikacje, aby je naprawiać - programistę.


DBNull to nie to samo co Nic

For Each row As DataGridViewRow In dgvPlanning.Rows
    If Not IsDBNull(row.Cells(0).Value) Then
        ...

The IsDBNull funkcja służy do testowania, czy wartość równa się System.DBNull: Z MSDN:

Wartość System.DBNull wskazuje, że obiekt reprezentuje brakujące lub nieistniejące dane. DBNull to nie to samo co Nic, co oznacza, że ​​zmienna nie została jeszcze zainicjalizowana.

Zaradzić

If row.Cells(0) IsNot Nothing Then ...

Tak jak poprzednio, możesz przetestować dla Nic, a następnie dla określonej wartości:

If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then

Przykład 2

Dim getFoo = (From f In dbContext.FooBars
               Where f.something = something
               Select f).FirstOrDefault

If Not IsDBNull(getFoo) Then
    If IsDBNull(getFoo.user_id) Then
        txtFirst.Text = getFoo.first_name
    Else
       ...

FirstOrDefault zwraca pierwszy element lub wartość domyślną, która jest Nothing dla typów odniesienia i nigdy DBNull:

If getFoo IsNot Nothing Then...

Sterownica

Dim chk As CheckBox

chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
    Return chk
End If

Jeśli CheckBox z chkName nie można znaleźć (lub istnieje w GroupBox), następnie chk nic nie będzie i próba odwołania się do dowolnej właściwości spowoduje wyjątek.

Zaradzić

If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...

DataGridView

DGV ma kilka dziwactw widzianych okresowo:

dgvBooks.DataSource = loan.Books
dgvBooks.Columns("ISBN").Visible = True       ' NullReferenceException
dgvBooks.Columns("Title").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Author").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Price").DefaultCellStyle.Format = "C"

Gdyby dgvBooks ma AutoGenerateColumns = True, utworzy kolumny, ale ich nie nazwał, więc powyższy kod zawodzi, gdy odwołuje się do nich po nazwie.

Zaradzić

Nazwij kolumny ręcznie lub odwołaj według indeksu:

dgvBooks.Columns(0).Visible = True

Przykład 2 - Uważaj na NewRow

xlWorkSheet = xlWorkBook.Sheets("sheet1")

For i = 0 To myDGV.RowCount - 1
    For j = 0 To myDGV.ColumnCount - 1
        For k As Integer = 1 To myDGV.Columns.Count
            xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
            xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
        Next
    Next
Next

Kiedy twój DataGridView ma AllowUserToAddRows tak jak True (domyślnie), Cells w pustym / nowym wierszu na dole wszystkie będą zawierać Nothing. Większość prób użycia zawartości (na przykład ToString) spowoduje NRE.

Zaradzić

Użyć For/Each zapętlić i przetestować IsNewRow Właściwość, aby określić, czy jest to ostatni wiersz. Działa to, czy AllowUserToAddRows jest prawdziwe, czy nie:

For Each r As DataGridViewRow in myDGV.Rows
    If r.IsNewRow = False Then
         ' ok to use this row

Jeśli używasz a For n pętli, zmodyfikuj liczbę wierszy lub użyj Exit For gdy IsNewRow jest prawdziwy.


Moje ustawienia (kolekcja ciągów)

W pewnych okolicznościach, próbując użyć przedmiotu z My.Settings który jest StringCollection może spowodować NullReference przy pierwszym użyciu. Rozwiązanie jest takie samo, ale nie tak oczywiste. Rozważać:

My.Settings.FooBars.Add("ziggy")         ' foobars is a string collection

Ponieważ VB zarządza ustawieniami dla ciebie, rozsądnie jest oczekiwać, że zainicjuje kolekcję. Będzie tak, ale tylko wtedy, gdy wcześniej dodałeś początkowy wpis do kolekcji (w edytorze ustawień). Ponieważ kolekcja jest (podobno) zainicjowana po dodaniu elementu, pozostaje Nothing gdy nie ma żadnych elementów w edytorze ustawień do dodania.

Zaradzić

Zainicjuj kolekcję ustawień w formularzu Load obsługa zdarzeń, jeśli / kiedy potrzeba:

If My.Settings.FooBars Is Nothing Then
    My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If

Zazwyczaj Settings kolekcja będzie musiała zostać zainicjowana dopiero po pierwszym uruchomieniu aplikacji. Alternatywnym środkiem zaradczym jest dodanie wartości początkowej do kolekcji Projekt -> Ustawienia | FooBars, zapisz projekt, a następnie usuń fałszywą wartość.


Kluczowe punkty

Prawdopodobnie zapomniałeś New operator.

lub

Coś, co przypuszczalnie działałoby bezbłędnie, aby zwrócić zainicjowany obiekt do kodu, nie było.

Nie ignoruj ​​ostrzeżeń kompilatora (nigdy) i użycia Option Strict On (zawsze).


Wyjątek MSN NullReference


273





Innym scenariuszem jest rzutowanie obiektu zerowego na typ wartości. Na przykład poniższy kod:

object o = null;
DateTime d = (DateTime)o;

To rzuci a NullReferenceException na obsadzie. Wydaje się to dość oczywiste w powyższym przykładzie, ale może się to zdarzyć w bardziej "późnych wiązaniach" zawiłych scenariuszach, w których obiekt zerowy został zwrócony z jakiegoś kodu, którego nie posiadasz, a rzutowanie jest na przykład generowane przez jakiś automatyczny system.

Jednym z przykładów jest ten prosty fragment wiążący ASP.NET z formantem Calendar:

<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />

Tutaj, SelectedDate jest w rzeczywistości własnością - of DateTime typ - z Calendar Typ Kontroli sieci Web i powiązanie może całkowicie zwrócić wartość zerową. Niejawny generator ASP.NET utworzy fragment kodu, który będzie równoznaczny z powyższym kodem. A to podniesie a NullReferenceException jest to dość trudne do wykrycia, ponieważ leży w generowanym przez ASP.NET kodzie, który kompiluje ...


217



Ładny chwyt. Jednoliniowy sposób uniknięcia: DateTime x = (DateTime) o as DateTime? ?? defaultValue; - Serge Shultz


Oznacza to, że dana zmienna jest wskazywana na nic. Mogłem wygenerować to tak:

SqlConnection connection = null;
connection.Open();

To spowoduje błąd, ponieważ podczas gdy zadeklarowałem zmienną "connection", nie jest wskazany na nic, kiedy próbuję zadzwonić do członka"Open", nie ma żadnego odniesienia do rozwiązania, a to spowoduje błąd.

Aby uniknąć tego błędu:

  1. Zawsze inicjuj swoje obiekty, zanim spróbujesz coś z nimi zrobić.
  2. Jeśli nie masz pewności, czy obiekt jest pusty, zaznacz go object == null.

Narzędzie Resharper firmy JetBrains rozpoznaje każde miejsce w kodzie, które może mieć zerowy błąd odniesienia, umożliwiając wprowadzenie zerowego sprawdzenia. Ten błąd jest źródłem błędów IMHO.


146



Narzędzie Resharper firmy JetBrains rozpoznaje każde miejsce w kodzie, które może mieć zerowy błąd odniesienia. To jest niepoprawne. Mam rozwiązanie bez tego wykrywania, ale kod czasami okazuje się wyjątek. Podejrzewam, że jest to czasami niewykrywalne - przynajmniej przez nich - w trakcie wielowątkowości, ale nie mogę dalej komentować, ponieważ nie zidentyfikowałem jeszcze lokalizacji mojego błędu. - j riv
Ale jak go rozwiązać, gdy pojawia się wyjątek NullReferenceException HttpContext.Current.Responce.Clear (). Nie rozwiązuje go żadne z powyższych rozwiązań. ponieważ podczas tworzenia obiektu obiektu HttpContext pojawia się błąd "Rozładowanie nie powiodło się, ponieważ niedostępny" Nowy "akceptuje tę liczbę argumentów. - Sunny Sandeep


Oznacza to, że twój kod użył zmiennej referencyjnej obiektu, która została ustawiona na wartość null (tzn. Nie odnosiła się do rzeczywistej instancji obiektu).

Aby zapobiec błędowi, obiekty, które mogą być puste, powinny zostać przetestowane na zero przed użyciem.

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
}

135





Należy pamiętać, że niezależnie od scenariusza przyczyna jest zawsze taka sama w .NET:

Próbujesz użyć zmiennej referencyjnej, której wartością jest Nothing/null. Kiedy wartość jest Nothing/null w przypadku zmiennej referencyjnej oznacza to, że w rzeczywistości nie zawiera odwołania do instancji dowolnego obiektu, który istnieje na stercie.

Nigdy nie przypisałeś czegoś do zmiennej, nigdy nie stworzyłeś instancji wartości przypisanej do zmiennej lub ustawiłeś zmienną równą Nothing/null ręcznie lub wywołałeś funkcję, która ustawiła zmienną na Nothing/null dla Ciebie.


90





Przykładem tego wyjątku jest: Kiedy próbujesz coś sprawdzić, jest to wartość pusta.

Na przykład:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 

Środowisko wykonawcze .NET wygeneruje wyjątek NullReferenceException, gdy spróbujesz wykonać akcję na coś, co nie zostało utworzone, tj. Powyższy kod.

W porównaniu do ArgumentNullException, który jest zwykle zgłaszany jako miara defensywna, jeśli metoda spodziewa się, że to, co jest do niego przekazywane, nie ma wartości null.

Więcej informacji jest w C # NullReferenceException i parametr Null.


76





Jeśli nie zainicjowałeś typu referencyjnego i chcesz ustawić lub odczytać jedną z jego właściwości, to wyrzucisz a Wyjątek NullReferenceException.

Przykład:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Możesz tego uniknąć, sprawdzając, czy zmienna nie ma wartości NULL:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

Aby w pełni zrozumieć, dlaczego generowany jest wyjątek NullReferenceException, ważne jest, aby znać różnicę między typy wartości i typy referencyjne.

Tak więc, jeśli masz do czynienia typy wartości, NullReferenceExceptions może nie pojawić się. Chociaż musisz zachować czujność w kontaktach typy referencyjne!

Tylko typy odniesienia, jak sugeruje nazwa, mogą zawierać referencje lub dosłownie nic (lub "zero"). Natomiast typy wartości zawsze zawierają wartość.

Typy referencyjne (te muszą być zaznaczone):

  • dynamiczny
  • obiekt
  • strunowy

Typy wartości (możesz je po prostu zignorować):

  • Typy liczbowe
  • Typy całkowe
  • Typy zmiennoprzecinkowe
  • dziesiętny
  • bool
  • Struktury zdefiniowane przez użytkownika

72



-1: ponieważ pytanie brzmi "Co to jest wyjątek NullReferenceException", typy wartości nie są istotne. - John Saunders
@John Saunders: Nie zgadzam się. Jako programista bardzo ważne jest, aby móc odróżnić wartości i typy referencyjne. w przeciwnym razie ludzie sprawdzą, czy liczby całkowite są zerowe. - Fabian Bigler
To prawda, tylko nie w kontekście tego pytania. - John Saunders
Dzięki za podpowiedź. Poprawiłem trochę i dodałem przykład na górze. Nadal uważam, że warto wspomnieć o Reference i Value Types. - Fabian Bigler
Myślę, że nie dodałeś niczego, czego nie było w innych odpowiedziach, ponieważ pytanie wstępnie zakłada typ odniesienia. - John Saunders


Kolejny przypadek, w którym NullReferenceExceptions może się zdarzyć (nieprawidłowe) użycie as operator:

class Book {
    public string Name { get; set; }
}
class Car { }

Car mycar = new Car();
Book mybook = mycar as Book;   // Incompatible conversion --> mybook = null

Console.WriteLine(mybook.Name);   // NullReferenceException

Tutaj, Book i Car są niekompatybilnymi typami; za Car nie można konwertować / rzutować na Book. Kiedy ta obsada nie powiedzie się, as zwraca null. Za pomocą mybook po tym powoduje a NullReferenceException.

Generalnie powinieneś użyć obsady lub as, jak następuje:

Jeśli spodziewasz się, że konwersja typu zawsze zakończy się sukcesem (tzn. Wiesz, jaki obiekt powinien być przed czasem), powinieneś użyć rzutowania:

ComicBook cb = (ComicBook)specificBook;

Jeśli nie jesteś pewien rodzaju, ale chcesz próbować używać go jako określonego typu, a następnie użyć as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}

65



To może się zdarzyć dużo, kiedy rozpakowywanie zmienna. Zdarza mi się, że zdarza się to często w programach obsługi zdarzeń po zmianie typu elementu interfejsu użytkownika, ale zapomnę zaktualizować kod z tyłu. - Brendan


Używany jest obiekt zawierający odwołanie do wartości pustej. Podaje więc zerowy wyjątek. W przykładzie wartość ciągu jest pusta i podczas sprawdzania jej długości wystąpił wyjątek.

Przykład:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}

Błąd wyjątku to:

Nieobsługiwany wyjątek:

System.NullReferenceException: Odwołanie do obiektu nie jest ustawione na instancję   obiektu. w Program.Main ()


59



Jak głęboko! Nigdy nie uważałem stałej "zerowej" za wartość odniesienia. Więc w ten sposób C # streszcza "NullPointer" huh? B / c jak pamiętam w C ++, NPE może być spowodowane przez wyłuskiwanie niezainicjowanego wskaźnika (np. Typ ref w c #), którego domyślną wartością jest adres, który nie jest przydzielony do tego procesu (w wielu przypadkach byłby to 0, szczególnie w późniejszych wersjach C ++, które dokonały autoinicjalizacji, która należy do systemu operacyjnego - z tym i umrzeć (lub po prostu złapać sigkill, z jakim system operacyjny atakuje twój proces)). - samsara


Simon Mourier podał ten przykład:

object o = null;
DateTime d = (DateTime)o;  // NullReferenceException

gdzie an rozpakowywanie konwersja (obsada) od  object (lub z jednej z klas System.ValueType lub System.Enumlub z typu interfejsu) do typ wartości (inny niż Nullable<>) samo w sobie daje NullReferenceException.

W innym kierunku, a boks konwersja od za Nullable<> który ma HasValue równy false  do typ odniesienia, może dać null odniesienie, które następnie może prowadzić do NullReferenceException. Klasyczny przykład:

DateTime? d = null;
var s = d.ToString();  // OK, no exception (no boxing), returns ""
var t = d.GetType();   // Bang! d is boxed, NullReferenceException

Czasami boks odbywa się w inny sposób. Na przykład z tą nietypową metodą rozszerzenia:

public static void MyExtension(this object x)
{
  x.ToString();
}

poniższy kod będzie problematyczny:

DateTime? d = null;
d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.

Te przypadki powstają z powodu specjalnych reguł, których używa środowisko wykonawcze podczas boksowania Nullable<> instancje.


48