Mam trochę kodu, a kiedy się wykonuje, rzuca NullReferenceException
, mówiąc:
Odwołanie do obiektu nie jest ustawione na instancję obiektu.
Co to znaczy i co mogę zrobić, aby naprawić ten błąd?
Mam trochę kodu, a kiedy się wykonuje, rzuca NullReferenceException
, mówiąc:
Odwołanie do obiektu nie jest ustawione na instancję obiektu.
Co to znaczy i co mogę zrobić, aby naprawić ten błąd?
Odpowiedzi:
Próbujesz użyć czegoś, co jest null
(lub Nothing
w VB.NET). Oznacza to, że albo go ustawisznull
, albo nigdy nie ustawisz go na niczym.
Jak wszystko inne, null
zostaje przekazany. Jeśli jest null
w metodzie „A”, może to oznaczać, że metoda „B” przekazała metodę null
do „A”.
null
może mieć różne znaczenia:
NullReferenceException
.null
celowo, aby wskazać, że nie ma dostępnej znaczącej wartości. Zauważ, że C # ma pojęcie zerowalnych typów danych dla zmiennych (np. Tabele bazy danych mogą mieć pola zerowalne) - możesz przypisać null
do nich, aby wskazać, że nie ma w nim żadnej wartości, na przykład int? a = null;
tam, gdzie znak zapytania wskazuje, że może przechowywać null zmienna a
. Możesz to sprawdzić za pomocą if (a.HasValue) {...}
lub za pomocą if (a==null) {...}
. Zmienne dopuszczające wartości zerowe, takie jak a
ten przykład, umożliwiają dostęp do wartości a.Value
jawnie lub tak samo normalnie przez a
. a.Value
rzuca InvalidOperationException
zamiast zamiastNullReferenceException
jeśli a
Isnull
- powinieneś zrobić to wcześniej, tzn. jeśli masz inną zmienną nullable int b;
, powinieneś wykonywać zadania takie jak if (a.HasValue) { b = a.Value; }
lub krótsze if (a != null) { b = a; }
.Pozostała część tego artykułu jest bardziej szczegółowa i pokazuje błędy, które często popełniają programiści, co może prowadzić do NullReferenceException
.
runtime
Rzucanie NullReferenceException
zawsze oznacza to samo: próbujesz użyć odwołania, a odniesienia nie jest zainicjowana (lub był raz zainicjowany, ale to już nie zainicjowany).
Oznacza to, że referencja jest null
i nie można uzyskać dostępu do członków (takich jak metody) poprzez null
referencję. Najprostszy przypadek:
string foo = null;
foo.ToUpper();
Spowoduje to wyrzucenie a NullReferenceException
w drugiej linii, ponieważ nie można wywołać metody instancji ToUpper()
dla string
odwołania wskazującego nanull
.
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ąpi, obowiązują ogólne zasady debugowania w Visual Studio: umieszczaj strategiczne punkty przerwania i sprawdzaj zmienne , najeżdżając myszką na ich nazwy, otwierając ( Szybko) Obserwuj okno lub korzystając z różnych paneli debugowania, takich jak Locals i Auto.
Jeśli chcesz dowiedzieć się, gdzie jest lub nie jest ustawione odniesienie, kliknij jego nazwę prawym przyciskiem myszy i wybierz „Znajdź wszystkie odniesienia”. Następnie możesz umieścić punkt przerwania w każdej znalezionej lokalizacji i uruchomić program z dołączonym debugerem. Za każdym razem, gdy debugger przełamie taki punkt przerwania, musisz ustalić, czy oczekujesz, że odwołanie ma wartość inną niż null, sprawdź zmienną i sprawdź, czy wskazuje ona instancję, kiedy tego oczekujesz.
Postępując zgodnie z przebiegiem programu w ten sposób, możesz znaleźć lokalizację, w której instancja nie powinna mieć wartości NULL, i dlaczego nie jest poprawnie ustawiona.
Niektóre typowe scenariusze, w których można zgłosić wyjątek:
ref1.ref2.ref3.member
Jeśli ref1 lub ref2 lub REF3 jest zerowy, to dostaniesz NullReferenceException
. Jeśli chcesz rozwiązać problem, dowiedz się, który z nich jest pusty, 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
The HttpContext.Current
może być zerowa lub User
nieruchomość może być null, lubIdentity
nieruchomość może być zerowa.
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 zerowego podrzędnego (Osoba), możesz zainicjować je w konstruktorze obiektu nadrzędnego (Książka).
To samo dotyczy inicjalizatorów obiektów zagnieżdżonych:
Book b1 = new Book
{
Author = { Age = 45 }
};
To przekłada się na
Book b1 = new Book();
b1.Author.Age = 45;
Gdy new
używane jest słowo kluczowe, tworzy ono tylko nowe wystąpienie Book
, ale nie nowe Person
, więc Author
właściwość jest nadalnull
.
public class Person
{
public ICollection<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
Kolekcja zagnieżdżona Initializers
zachowuje się tak samo:
Person p1 = new Person
{
Books = {
new Book { Title = "Title1" },
new Book { Title = "Title2" },
}
};
To przekłada się na
Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });
new Person
Tylko tworzy instancję Person
, ale Books
kolekcja jest nadal null
. Initializer
Składnia kolekcji nie tworzy kolekcji p1.Books
, tylko tłumaczy nap1.Books.Add(...)
instrukcje.
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
}
}
###Bad Naming Conventions:
If you named fields differently from locals, you might have realized that you never initialized the field.
klasa publiczna Form1 {klient prywatny;
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 poprzedzać pola znakiem podkreślenia:
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!";
}
}
// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();
Jeśli wystąpi wyjątek podczas odwoływania się do właściwości @Model
w ASP.NET MVC View
, musisz zrozumieć, że Model
metoda ustawia się w metodzie akcji podczas return
przeglądania. Po zwróceniu pustego modelu (lub właściwości modelu) z kontrolera 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 -->
WPF
formanty są tworzone podczas połączenia InitializeComponent
w kolejności, w jakiej pojawiają się w drzewie wizualnym. A NullReferenceException
zostanie podniesiony w przypadku wcześniej utworzonych elementów sterujących za pomocą procedur obsługi zdarzeń itp., InitializeComponent
Które uruchamiają się, podczas których odwołują się elementy sterujące utworzone wcześniej.
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
. Jeśli comboBox1_SelectionChanged
spróbujesz odwołać się do `label1, 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
(tj. Umieszczenie label1
wcześniej comboBox1
, ignorowanie kwestii filozofii projektowania, przynajmniej rozwiązałoby problemNullReferenceException
tutaj.
as
var myThing = someObject as Thing;
Nie rzuca to, InvalidCastException
ale zwraca a, null
gdy rzutowanie się nie powiedzie (i gdy someObject
sam jest pusty). Więc bądź tego świadomy.
FirstOrDefault()
iSingleOrDefault()
Proste wersje First()
i Single()
wyjątki, gdy nic nie ma. W takim przypadku wersje „OrDefault” zwracają wartość null. Więc bądź tego świadomy.
foreach
rzuca, gdy próbujesz iterować pustą kolekcję. Zwykle spowodowane przez nieoczekiwany null
wynik metod zwracających kolekcje.
List<int> list = null;
foreach(var v in list) { } // exception
Bardziej realistyczny przykład - wybierz węzły z dokumentu XML. Wyrzuci, jeśli nie zostaną znalezione węzły, ale wstępne debugowanie pokazuje, że wszystkie właściwości są prawidłowe:
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
null
i zignoruj wartości puste.Jeśli spodziewasz się, że odwołanie ma czasami wartość NULL, możesz sprawdzić, czy istnieje ono null
przed uzyskaniem dostępu do członków instancji:
void PrintName(Person p)
{
if (p != null)
{
Console.WriteLine(p.Name);
}
}
null
i podaj wartość domyślną.Wywołane metody, które mają zostać zwrócone, mogą zwrócić instancję null
, na przykład gdy szukany obiekt 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;
}
null
wywołania metod i wyślij niestandardowy wyjątek.Możesz także rzucić niestandardowy wyjątek, aby złapać go 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;
}
Debug.Assert
jeśli wartość nigdy nie powinna byćnull
, aby złapać problem wcześniej niż wystąpi wyjątek.Jeśli podczas programowania wiesz, że metoda może, ale nigdy nie powinna powrócić null
, możesz użyć jej, Debug.Assert()
aby przerwać tak szybko, jak to możliwe, 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ż to sprawdzenie nie zakończy się w kompilacji wydania , co spowoduje, że wyrzuci go NullReferenceException
ponownie, gdy będzie book == null
w środowisku uruchomieniowym w trybie wydania.
GetValueOrDefault()
dla nullable
typów wartości, aby podać wartość domyślną, 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
??
[C #] lub If()
[VB].Skrót do podania wartości domyślnej w przypadku null
napotkania:
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;
}
?.
lub ?[x]
dla tablic (dostępne w C # 6 i VB.NET 14):Jest to również czasami nazywane operatorem bezpiecznej nawigacji lub Elvisa (po jego kształcie). Jeśli wyrażenie po lewej stronie operatora ma wartość NULL, to prawa strona nie będzie oceniana, a zamiast tego zwracana jest wartość NULL. Oznacza to, że takie przypadki:
var title = person.Title.ToUpper();
Jeśli dana osoba nie ma tytułu, spowoduje to zgłoszenie wyjątku, ponieważ próbuje ona wywołać ToUpper
właściwość o wartości zerowej.
W C# 5
i poniżej, może to być strzeżone z:
var title = person.Title == null ? null : person.Title.ToUpper();
Teraz zmienna tytułowa będzie miała wartość NULL zamiast zgłaszać wyjątek. C # 6 wprowadza krótszą składnię do tego:
var title = person.Title?.ToUpper();
Spowoduje to powstanie zmiennej tytułowej null
, a wywołanie do ToUpper
nie zostanie wykonane, jeśli tak person.Title
jest null
.
Oczywiście nadal musisz sprawdzić title
wartość null lub użyć operatora warunku zerowego razem z operatorem koalescencji zerowej ( ??
), 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 można używać ?[i]
w następujący sposób:
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 myIntArray
jest null, wyrażenie zwraca null i można go bezpiecznie sprawdzić. Jeśli zawiera tablicę, zrobi to samo co:
elem = myIntArray[i];
i zwróci i<sup>th</sup>
element.
Wprowadzone C# 8
tam typy zerowe i typy referencyjne zerowalne przeprowadzają analizę statyczną zmiennych i zapewniają ostrzeżenie kompilatora, jeśli wartość może być potencjalnie zerowa lub została ustawiona na null. Typy zerowalne pozwalają jawnie zezwolić, aby typy miały wartość null.
Kontekst adnotacji z dopuszczeniem wartości zerowej i kontekst ostrzeżenia o wartości dopuszczającej wartości zerowe można ustawić dla projektu za pomocą Nullable
elementu z csproj
pliku. Ten element konfiguruje sposób, w jaki kompilator interpretuje zerowanie typów oraz generowane ostrzeżenia. Prawidłowe ustawienia to:
Typ odwołania zerowalnego jest odnotowywany przy użyciu tej samej składni, co typy wartości dopuszczających wartości zerowe: a ?
jest dodawane do typu zmiennej.
C#
obsługuje „bloki iteratora” (zwane „generatorami” w niektórych innych popularnych językach). Wyjątki zerowania dereferencji mogą być szczególnie trudne do debugowania w blokach iteratora z powodu odroczonego 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) { ... }
Jeśli whatever
wyniki w null
to 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 źle? Ponieważ blok iteratora tak naprawdę nie działa aż do foreach
! Wywołanie GetFrobs
po prostu zwraca obiekt, który po iteracji uruchomi blok iteratora.
Pisząc czek zerowy w ten sposób, zapobiegasz zerowaniu, ale przenosisz wyjątek argumentu zerowego do punktu iteracji , a nie do punktu wywołania , a debugowanie jest bardzo mylące .
Prawidłowa 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();
}
To znaczy, stwórz prywatną metodę pomocniczą, która ma logikę bloku iteratora, i publiczną metodę powierzchniową, która sprawdza zero i zwraca iterator. Teraz, gdy GetFrobs
jest wywoływany, sprawdzanie wartości zerowej odbywa się natychmiast, a następnie GetFrobsForReal
wykonuje się, gdy sekwencja jest iterowana.
Jeśli sprawdzisz źródło odniesienia LINQ
do Obiektów, zobaczysz, że ta technika jest używana przez cały czas. Jest nieco bardziej niezręczny w pisaniu, ale znacznie ułatwia debugowanie błędów nieważności. Zoptymalizuj swój kod dla wygody dzwoniącego, a nie dla autora .
C#
ma tryb „niebezpieczny”, który, jak sama nazwa wskazuje, jest wyjątkowo niebezpieczny, ponieważ normalne mechanizmy bezpieczeństwa zapewniające bezpieczeństwo pamięci i bezpieczeństwo typu nie są egzekwowane. Nie powinieneś pisać niebezpiecznego kodu, chyba że dobrze rozumiesz, jak działa pamięć .
W trybie niebezpiecznym powinieneś wiedzieć o dwóch ważnych faktach:
Aby zrozumieć, dlaczego tak jest, pomaga zrozumieć, w jaki sposób .NET generuje wyjątki zerowania zerowania. (Te szczegóły dotyczą .NET działającego w systemie Windows; inne systemy operacyjne używają podobnych mechanizmów.)
Pamięć jest zwirtualizowana Windows
; każdy proces otrzymuje wirtualną pamięć wielu „stron” pamięci, które są śledzone przez system operacyjny. Każda strona pamięci ma ustawione flagi, które określają, w jaki sposób można jej używać: czytać, zapisywać, wykonywać i tak dalej. Najniższa strona jest oznaczony jako „produkują błąd, jeśli kiedykolwiek używany w jakikolwiek sposób”.
Zarówno wskaźnik zerowy, jak i odwołanie zerowe w C#
są wewnętrznie reprezentowane jako liczba zero, więc każda próba wyłuskiwania go w odpowiedniej pamięci powoduje, że system operacyjny powoduje błąd. Środowisko wykonawcze .NET wykrywa ten błąd i przekształca go w wyjątek zerowania.
Właśnie dlatego odsyłanie zarówno wskaźnika zerowego, jak i odwołania zerowego powoduje ten sam wyjątek.
A co z drugim punktem? Odwołanie do nieprawidłowego wskaźnika, który znajduje się na najniższej stronie pamięci wirtualnej, powoduje ten sam błąd systemu operacyjnego, a tym samym ten sam wyjątek.
Dlaczego to ma sens? Załóżmy, że mamy strukturę zawierającą dwie liczby całkowite i niezarządzany wskaźnik równy null. Jeśli spróbujemy wyrejestrować drugą liczbę CLR
całkowitą w strukturze, nie spróbuje ona uzyskać dostępu do pamięci w lokalizacji zero; uzyska dostęp do magazynu w czwartej lokalizacji. Ale logicznie jest to dereferencja zerowa, ponieważ docieramy do tego adresu za pomocą wartości zerowej.
Jeśli pracujesz z niebezpiecznym kodem i otrzymujesz wyjątek zerowania, po prostu pamiętaj, że obrażający wskaźnik nie musi mieć wartości zerowej. Może to być dowolna lokalizacja na najniższej stronie, a ten wyjątek zostanie wygenerowany.
new Book { Author = { Age = 45 } };
Jak działa wewnętrzna inicjalizacja ... Nie mogę sobie wyobrazić sytuacji, w której wewnętrzna inicjacja kiedykolwiek działałaby, a jednak kompiluje się i działa intellisense ... Chyba że dla struktur?
NullReference Exception
Dla Visual Basic nie różni się od tej w języku C # . W końcu oboje zgłaszają ten sam wyjątek zdefiniowany w .NET Framework, którego oboje używają. Przyczyny unikalne dla Visual Basic są rzadkie (być może tylko jedna).
Ta odpowiedź będzie używać terminów, składni i kontekstu Visual Basic. Użyte przykłady pochodzą z wielu poprzednich pytań o przepełnienie stosu. To jest maksymalizacja trafności za pomocą rodzaju sytuacje często postrzegane w postach. Dla tych, którzy mogą go potrzebować, podano nieco więcej wyjaśnień. Przykładem podobne do Ciebie jest bardzo prawdopodobne tutaj wymienione.
Uwaga:
NullReferenceException
(NRE), jak go znaleźć, jak go naprawić i jak go uniknąć. NRE może być wywołany na wiele sposobów, więc prawdopodobnie nie będzie to twoje jedyne spotkanie.Komunikat „Obiekt nie jest ustawiony na instancję obiektu” oznacza, że próbujesz użyć obiektu, który nie został zainicjowany. Sprowadza się to do jednego z tych:
Ponieważ problemem jest odwołanie do obiektu, dlatego Nothing
odpowiedzią jest ich zbadanie, aby dowiedzieć się, który z nich. Następnie określ, dlaczego nie został zainicjowany. Przytrzymaj wskaźnik myszy nad różnymi zmiennymi, a Visual Studio (VS) wyświetli ich wartości - sprawcą będzie Nothing
.
Powinieneś także usunąć wszystkie bloki Try / Catch z odpowiedniego kodu, szczególnie te, w których nic nie ma w bloku Catch. Spowoduje to awarię kodu podczas próby użycia obiektu, który jest Nothing
. To jest to, czego chcesz, ponieważ określi dokładną lokalizację problemu i pozwoli zidentyfikować obiekt, który go powoduje.
A MsgBox
in the Catch, który wyświetla, Error while...
będzie mało pomocny. Ta metoda również prowadzi do bardzo złych pytań o przepełnienie stosu, ponieważ nie można opisać faktycznego wyjątku, zaangażowanego obiektu ani nawet wiersza kodu, w którym ma to miejsce.
Możesz także użyć Locals Window
( Debuguj -> Windows -> Locals ), aby zbadać swoje obiekty.
Gdy wiesz, co i gdzie jest problem, zazwyczaj jest to dość łatwe do naprawienia i szybsze niż opublikowanie nowego pytania.
Zobacz też:
Dim reg As CashRegister
...
TextBox1.Text = reg.Amount ' NRE
Problem polega na tym, Dim
że nie tworzy obiektu CashRegister ; deklaruje tylko zmienną reg
tego typu. Zadeklarowanie zmiennej obiektu i utworzenie instancji to dwie różne rzeczy.
Zaradzić
New
Operator często mogą być wykorzystywane do tworzenia instancji, gdy zadeklarujesz go:
Dim reg As New CashRegister ' [New] creates instance, invokes the constructor
' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister
Jeśli odpowiednie jest utworzenie instancji później:
Private reg As CashRegister ' Declare
...
reg = New CashRegister() ' Create instance
Uwaga: Nie używaj Dim
ponownie w procedurze, łącznie z konstruktorem ( Sub New
):
Private reg As CashRegister
'...
Public Sub New()
'...
Dim reg As New CashRegister
End Sub
Spowoduje to utworzenie zmiennej lokalnejreg
, która istnieje tylko w tym kontekście (pod). reg
Zmiennej z poziomu modułu Scope
, który będzie używany wszędzie indziej szczątki Nothing
.
Brak
New
operatora jest przyczyną nr 1NullReference Exceptions
sprawdzonych pytań o przepełnienie stosu.Visual Basic próbuje wielokrotnie wyjaśniać proces za pomocą
New
: UżycieNew
operatora tworzy nowy obiekt i wywołujeSub New
konstruktor, w którym obiekt może wykonać dowolną inną inicjalizację.
Dla jasności Dim
(lub Private
) deklaruje tylko zmienną i jej zmienną Type
. Zakres zmiennej - czy istnieje ona dla całego modułu / klasy lub jest lokalny procedurze - określa się , gdzie jest ona zadeklarowana. Private | Friend | Public
określa poziom dostępu, a nie zakres .
Aby uzyskać więcej informacji, zobacz:
Tablice muszą być również tworzone:
Private arr as String()
Ta tablica została tylko zadeklarowana, a nie utworzona. Istnieje kilka sposobów inicjalizacji 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 tablicy lokalnej za pomocą literału Option Infer
, elementy As <Type>
i New
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 tablicy są wywnioskowane z przypisywanych danych. Deklaracje poziomie modułu / klasa 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 Foo
obiektów w niej nie ma.
Zaradzić
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i) = New Foo() ' Create Foo instance
arrFoo(i).Bar = i * 10
Next
Użycie a List(Of T)
utrudni posiadanie elementu bez ważnego 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:
Kolekcje .NET (których jest wiele odmian - Listy, Słownik itp.) Również muszą zostać utworzone lub utworzone.
Private myList As List(Of String)
..
myList.Add("ziggy") ' NullReference
Otrzymujesz ten sam wyjątek z tego samego powodu - myList
został tylko zadeklarowany, ale nie utworzono żadnej instancji. Środek zaradczy jest taki sam:
myList = New List(Of String)
' Or create an instance when declared:
Private myList As New List(Of String)
Częstym nadzorem 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 z procedur spowoduje NRE, ponieważ barList
jest tylko deklarowana, a nie tworzona. Utworzenie wystąpienia Foo
nie spowoduje również utworzenia wystąpienia wewnętrznego barList
. Być może zamierzano to zrobić 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 .
Praca z bazami danych stwarza wiele możliwości dla NullReference ponieważ może istnieć wiele obiektów ( Command
, Connection
, Transaction
, Dataset
, DataTable
, DataRows
....) w użytku od razu. Uwaga: Nie ma znaczenia, jakiego dostawcy danych używasz - MySQL, SQL Server, OleDB itp. - pojęcia 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 Dataset został zadeklarowany, ale instancja nigdy nie została utworzona. DataAdapter
Wypełni istniejącą DataSet
, nie utworzyć. W tym przypadku, ponieważ ds
jest to zmienna lokalna, IDE ostrzega, że może się to zdarzyć:
Po zadeklarowaniu jako zmienna na poziomie modułu / klasy, jak się wydaje con
, kompilator nie może wiedzieć, czy obiekt został utworzony za pomocą procedury poprzedzającej. 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)
Literówka jest problemem tutaj: Employees
vs Employee
. Nie DataTable
utworzono nazwy „Pracownik”, więc NullReferenceException
wyniki próbują uzyskać do niej dostęp. Innym potencjalnym problemem jest założenie, że Items
nie będzie, gdy SQL zawiera klauzulę WHERE.
Zaradzić
Ponieważ używa to jednej tabeli, użycie Tables(0)
pozwoli uniknąć błędów ortograficznych. 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
dotkniętych, które można również przetestować:
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
DataAdapter
Zapewni TableNames
jak pokazano w poprzednim przykładzie, ale nie nazwiska zanalizować z tabeli SQL lub bazy danych. W rezultacie,ds.Tables("TICKET_RESERVATION")
odwołuje się do nieistniejącej tabeli.
Rozwiązanie jest takie samo, odwołać tabelę indeksu:
If ds.Tables(0).Rows.Count > 0 Then
Zobacz także klasę DataTable .
If myFoo.Bar.Items IsNot Nothing Then
...
Kod testuje tylko Items
oba elementy myFoo
i Bar
może być również niczym. Lekarstwem jest sprawdzenie całego łańcucha lub ścieżkę obiektów 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 zostaną wykonane po False
spełnieniu pierwszego warunku. Pozwala to kodowi bezpiecznie „wiercić” w obiekcie (ach) jeden „poziom” na raz, oceniając myFoo.Bar
dopiero po (i jeśli) myFoo
ustaleniu, że jest poprawny. Łańcuchy obiektów lub ścieżki mogą być 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 „poniżej” null
obiektu. Dotyczy to również kontroli:
myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
Tutaj, myWebBrowser
lub Document
może być nic lub formfld1
element nie może istnieć.
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)
Między innymi ten kod nie przewiduje, że użytkownik mógł nie wybrać czegoś w co najmniej jednym elemencie sterującym interfejsu użytkownika. ListBox1.SelectedItem
może być Nothing
, więc ListBox1.SelectedItem.ToString
spowoduje NRE.
Zaradzić
Sprawdź poprawność danych przed ich użyciem (także Option Strict
parametrami use i 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...
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 sposobu kodowania, IDE zgłosi, że Controls
nie istnieje w bieżącym kontekście lub „nie może odwoływać się do elementu niestatycznego”. Tak więc do pewnego stopnia jest to sytuacja wyłącznie VB. Jest to również złożone, ponieważ może doprowadzić do kaskady awarii.
Tablice i kolekcje nie mogą być inicjowane w ten sposób. Ten kod inicjujący zostanie uruchomiony, zanim konstruktor utworzy znak Form
lub Controls
. W rezultacie:
somevar
Przypisanie spowoduje natychmiastowego NRE bo nic nie posiada .Text
własnośćOdwołanie do elementów tablicy później spowoduje NRE. Jeśli zrobisz to z Form_Load
powodu dziwnego błędu, IDE może nie zgłosić wyjątku, kiedy to się stanie. Wyjątek pojawi się później, gdy kod będzie próbował użyć tablicy. Ten „cichy wyjątek” został szczegółowo opisany w tym poście . Dla naszych celów kluczem jest to, że gdy dzieje się coś katastrofalnego podczas tworzenia formularza ( Sub New
lub Form Load
zdarzenia), wyjątki mogą nie zostać zgłoszone, kod kończy procedurę i wyświetla tylko formularz.
Ponieważ żaden inny kod w Twoim Sub New
lub Form Load
zdarzeniu nie będzie działał po NRE, wiele innych rzeczy może pozostać niezainicjowanych.
Sub Form_Load(..._
'...
Dim name As String = NameBoxes(2).Text ' NRE
' ...
' More code (which will likely not be executed)
' ...
End Sub
Uwaga: dotyczy to wszystkich odniesień do kontroli i komponentów, które czynią je nielegalnymi, gdy 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ęściowe rozwiązanie
To ciekawe, że VB nie daje ostrzeżenie, ale rozwiązaniem jest zadeklarować pojemniki na poziomie formy, ale zainicjować ich obsługi zdarzeń obciążenia forma gdy kontrole zrobić istnieć. Można to zrobić, Sub New
dopóki kod jest po InitializeComponent
wywołaniu:
' 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 wyjść z lasu. Wszelkie formanty znajdujące się w kontrolce kontenera (jak a GroupBox
lub Panel
) nie zostaną znalezione w Me.Controls
; będą znajdować się w kolekcji Controls tego panelu lub GroupBox. Formant nie zostanie również zwrócony, gdy nazwa formantu zostanie źle napisana ( "TeStBox2"
). W takich przypadkach Nothing
ponownie zostaną zapisane w tych elementach tablicy, a podczas próby odniesienia do nich powstanie NRE.
Powinny być łatwe do znalezienia teraz, gdy wiesz, czego szukasz:
„Button2” znajduje się na Panel
Zaradzić
Zamiast pośrednich odniesień według nazwy przy użyciu Controls
kolekcji formularza , użyj odwołania kontrolnego:
' 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...})
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 ostrzega, że „ nie wszystkie ścieżki zwracają wartość i NullReferenceException
może wynikać ”. Możesz wyłączyć ostrzeżenie, zastępując Exit Function
je Return Nothing
, ale to nie rozwiązuje problemu. Wszystko, co próbuje użyć return, kiedy someCondition = False
spowoduje NRE:
bList = myFoo.BarList()
For Each b As Bar in bList ' EXCEPTION
...
Zaradzić
Zamień Exit Function
w funkcji na Return bList
. Zwracanie pustego List
nie jest tym samym, co zwracanie Nothing
. Jeśli istnieje szansa, że zwrócony obiekt może być Nothing
, przetestuj go przed użyciem:
bList = myFoo.BarList()
If bList IsNot Nothing Then...
Źle zaimplementowana funkcja Try / Catch może ukryć problem i doprowadzić do powstania 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, w którym obiekt nie jest tworzony zgodnie z oczekiwaniami, ale pokazuje także przydatność licznika pustego Catch
.
W kodzie SQL występuje dodatkowy przecinek (po „adresie mailowym”), co powoduje wyjątek o nazwie .ExecuteReader
. Po tym, Catch
jak nic nie robi, Finally
próbuje wykonać czyszczenie, ale ponieważ nie można Close
wyzerować DataReader
obiektu, wyniki są zupełnie nowe NullReferenceException
.
Pusty Catch
blok to diabelski plac zabaw. Ten PO był zaskoczony, dlaczego dostaje NRE w Finally
bloku. W innych sytuacjach pusta Catch
może skutkować czymś znacznie dalej idącym do szaleństwa i powodować, że spędzasz czas na szukaniu niewłaściwych rzeczy w niewłaściwym miejscu dla problemu. („Cichy wyjątek” opisany powyżej zapewnia tę samą wartość rozrywkową).
Zaradzić
Nie używaj pustych bloków Try / Catch - pozwól na awarię kodu, abyś mógł a) zidentyfikować przyczynę b) zidentyfikować lokalizację ic) zastosować odpowiednie rozwiązanie. Bloki Try / Catch nie mają na celu ukrycia wyjątków od osoby wyjątkowo wykwalifikowanej do ich naprawy - programisty.
For Each row As DataGridViewRow In dgvPlanning.Rows
If Not IsDBNull(row.Cells(0).Value) Then
...
IsDBNull
Funkcja służy do sprawdzania, czy dana wartość jest równa System.DBNull
: Od MSDN:
Wartość System.DBNull wskazuje, że Obiekt reprezentuje brakujące lub nieistniejące dane. DBNull to nie to samo co Nic, co wskazuje, że zmienna nie została jeszcze zainicjowana.
Zaradzić
If row.Cells(0) IsNot Nothing Then ...
Tak jak poprzednio, możesz przetestować na Nic, a następnie na określoną wartość:
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 dotyczy Nothing
typów referencyjnych i nigdy DBNull
:
If getFoo IsNot Nothing Then...
Dim chk As CheckBox
chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
Return chk
End If
Jeżeli CheckBox
ze chkName
nie można znaleźć (lub istnieje w GroupBox
), wówczas chk
będzie nic i być próbuje odwołać każdą własność spowoduje wyjątek.
Zaradzić
If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
DGV ma okresowo kilka dziwactw:
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"
Jeśli dgvBooks
tak AutoGenerateColumns = True
, utworzy kolumny, ale ich nie nazwie, więc powyższy kod zawodzi, gdy odwołuje się do nich po nazwie.
Zaradzić
Nazwij kolumny ręcznie lub odwołaj się do nich według indeksu:
dgvBooks.Columns(0).Visible = True
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
Gdy twój DataGridView
ma AllowUserToAddRows
jako True
(domyślny), wszystkie Cells
w pustym / nowym wierszu na dole będą zawierać Nothing
. Większość prób użycia zawartości (na przykład ToString
) spowoduje NRE.
Zaradzić
Użyj For/Each
pętli i przetestuj IsNewRow
właściwość, aby ustalić, czy jest to ostatni wiersz. Działa AllowUserToAddRows
to niezależnie od tego, czy jest prawdziwe:
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
Jeśli używasz For n
pętli, zmień liczbę wierszy lub użyj, Exit For
gdy IsNewRow
jest to prawda.
W pewnych okolicznościach próba użycia elementu, z My.Settings
którego 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 Twoimi ustawieniami, uzasadnione jest oczekiwanie, że zainicjuje on kolekcję. Będzie, ale tylko wtedy, gdy wcześniej dodałeś początkowy wpis do kolekcji (w edytorze ustawień). Ponieważ kolekcja jest (najwidoczniej) inicjowana podczas dodawania elementu, pozostaje, Nothing
gdy nie ma żadnych elementów w edytorze ustawień do dodania.
Zaradzić
Zainicjuj kolekcję ustawień w Load
module obsługi zdarzeń formularza , jeśli / w razie potrzeby:
If My.Settings.FooBars Is Nothing Then
My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If
Zazwyczaj Settings
kolekcję należy zainicjować tylko przy pierwszym uruchomieniu aplikacji. Alternatywnym rozwiązaniem jest dodanie początkowej wartości do kolekcji w Projekcie -> Ustawienia | FooBars , zapisz projekt, a następnie usuń fałszywą wartość.
Prawdopodobnie zapomniałeś New
operatora.
lub
Coś, co zakładałeś, że działałoby bezbłędnie, aby zwrócić zainicjowany obiekt do twojego kodu, nie zrobiło tego.
Nie ignoruj ostrzeżeń kompilatora (zawsze) i używaj Option Strict On
(zawsze).
Innym scenariuszem jest rzutowanie pustego obiektu na typ wartości . Na przykład poniższy kod:
object o = null;
DateTime d = (DateTime)o;
Rzuci a NullReferenceException
na obsadę. Wydaje się to dość oczywiste w powyższej próbce, ale może się to zdarzyć w bardziej skomplikowanych scenariuszach z „późnym wiązaniem”, w których obiekt zerowy został zwrócony z 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ązania ASP.NET z kontrolką Kalendarz:
<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />
Tutaj, SelectedDate
w rzeczywistości jest nieruchomość - od DateTime
typu - o Calendar
kontroli typu Web, a wiązanie mogłaby doskonale powrócić coś null. Domniemany generator ASP.NET utworzy fragment kodu, który będzie równoważny powyższemu rzutowanemu kodowi. A to podniesieNullReferenceException
dość trudny do wykrycia, ponieważ leży w kodzie wygenerowanym przez ASP.NET, który dobrze się kompiluje ...
DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Oznacza to, że dana zmienna nie jest wskazywana na nic. Mógłbym to wygenerować tak:
SqlConnection connection = null;
connection.Open();
Spowoduje to błąd, ponieważ podczas gdy zadeklarowałem zmienną „connection
”, nie jest na nic wskazywana. Kiedy próbuję zadzwonić do członka „ Open
”, nie ma odniesienia do rozwiązania, a błąd zostanie zgłoszony.
Aby uniknąć tego błędu:
object == null
.Narzędzie Resharper JetBrains zidentyfikuje każde miejsce w kodzie, w którym istnieje możliwość wystąpienia błędu zerowego odniesienia, umożliwiając sprawdzenie wartości zerowej. Ten błąd jest głównym źródłem błędów, IMHO.
Oznacza to, że Twój kod używał zmiennej odwołania do obiektu, która została ustawiona na null (tzn. Nie odwoływała się do rzeczywistej instancji obiektu).
Aby uniknąć błędu, przed użyciem należy przetestować obiekty, które mogą mieć wartość NULL.
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
}
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
. Gdy wartością jestNothing
/null
dla zmiennej referencyjnej, oznacza to, że tak naprawdę nie przechowuje ona referencji do instancji dowolnego obiektu istniejącego na stercie.Albo nigdy nie przypisałeś czegoś do zmiennej, nigdy nie utworzyłeś instancji wartości przypisanej do zmiennej, lub ustawiłeś zmienną na wartość
Nothing
/null
ręcznie, lub wywołałeś funkcję, która ustawiła zmienną naNothing
/null
dla ciebie.
Przykład zgłaszanego wyjątku to: Gdy próbujesz coś sprawdzić, jest to zerowe.
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 zgłosi wyjątek NullReferenceException, gdy spróbujesz wykonać akcję na czymś, co nie zostało utworzone, np. Powyższy kod.
W porównaniu z ArgumentNullException, który jest zwykle rzucany jako środek obronny, jeśli metoda oczekuje, że to, co jest przekazywane, nie jest zerowe.
Więcej informacji znajduje się w C # NullReferenceException i Null Parameter .
Aktualizacja C # 8.0, 2019: typy odniesienia zerowalne
C # 8.0 wprowadza typy odwołania zerowalne i typy zerowalne . Dlatego należy sprawdzić tylko typy zerowalne, aby uniknąć wyjątku NullReferenceException .
Jeśli nie zainicjowałeś typu odwołania i chcesz ustawić lub odczytać jedną z jego właściwości, wygeneruje wyjątek NullReferenceException .
Przykład:
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
Można tego po prostu 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 zgłaszany jest wyjątek NullReferenceException, ważne jest, aby znać różnicę między nimi typami wartości a [typami referencyjnymi] [3].
Więc jeśli masz do czynienia z typami wartości , NullReferenceExceptions może nie wystąpić. Chociaż musisz zachować czujność, gdy masz do czynienia typami referencyjnymi !
Tylko typy referencyjne, jak sama nazwa wskazuje, mogą zawierać referencje lub dosłownie wskazywać na nic (lub „null”). Podczas gdy typy wartości zawsze zawierają wartość.
Typy referencyjne (te należy sprawdzić):
Typy wartości (możesz po prostu zignorować te):
Innym przypadkiem, w którym NullReferenceExceptions
może się zdarzyć, jest (nieprawidłowe) użycie as
operatora :
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
W tym przypadku, Book
i Car
to niezgodne rodzajów; Car
nie można przekształcić / rzutowany na Book
. Gdy rzutowanie nie powiedzie się, as
powraca null
. Używanie mybook
później powodujeNullReferenceException
.
Ogólnie rzecz biorąc, powinieneś użyć obsady lub as
:
Jeśli oczekujesz, że konwersja typu zawsze się powiedzie (tzn. Wiesz, co obiekt powinien wyprzedzić), powinieneś użyć rzutowania:
ComicBook cb = (ComicBook)specificBook;
Jeśli nie masz pewności co do typu, ale chcesz spróbować użyć go jako określonego typu, użyj as
:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
Używasz obiektu zawierającego odwołanie do wartości zerowej. Daje to zerowy wyjątek. W tym przykładzie wartość ciągu jest pusta i podczas sprawdzania jego 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 ()
Podczas gdy to, co powoduje NullReferenceExceptions i podejścia do unikania / naprawiania takiego wyjątku, zostały omówione w innych odpowiedziach, wielu programistów jeszcze się nie nauczyło, jak samodzielnie debugować takie wyjątki podczas programowania.
W Visual Studio jest to zwykle łatwe dzięki Visual Studio Debugger .
Po pierwsze, upewnij się, że wychwycony zostanie prawidłowy błąd - patrz Jak zezwolić na zerwanie w „System.NullReferenceException” w VS2010? Uwaga 1
Następnie rozpocznij od debugowania (F5) lub dołącz [debugger VS] do uruchomionego procesu . Czasami może być użytecznyDebugger.Break
, które wyświetli monit o uruchomienie debuggera.
Teraz, gdy zostanie zgłoszony (lub nieobsługiwany) wyjątek NullReferenceException, debugger zatrzyma się (pamiętasz powyższą regułę?) W linii, w której wystąpił wyjątek. Czasami błąd będzie łatwy do wykrycia.
Na przykład w poniższym wierszu jedynym kodem, który może spowodować wyjątek, jest myString
wartość zero. Można to zweryfikować, patrząc na okno obserwacyjne lub uruchamiając wyrażenia w oknie natychmiastowym .
var x = myString.Trim();
W bardziej zaawansowanych przypadkach, takich jak poniższe, musisz użyć jednej z powyższych technik (Obejrzyj lub Natychmiastowy system Windows), aby sprawdzić wyrażenia w celu ustalenia, czy jest str1
to null, czy jeśli str2
było null.
var x = str1.Trim() + str2.Trim();
Raz gdzie wyjątek jest rzut został zlokalizowany, to zazwyczaj banalne do rozumu do tyłu, aby dowiedzieć się, gdzie wartość NULL [nieprawidłowo] wprowadzono -
Poświęć czas na zrozumienie przyczyny wyjątku. Sprawdź wyrażenia zerowe. Sprawdź poprzednie wyrażenia, które mogły spowodować takie wyrażenia zerowe. Dodaj punkty przerwania i wykonaj odpowiednie kroki programu. Użyj debugera.
1 Jeśli Break on Throws jest zbyt agresywny, a debugger zatrzymuje się na NPE w bibliotece .NET lub w bibliotece innej firmy, Break on Unhandled może być wykorzystany do ograniczenia przechwyconych wyjątków. Dodatkowo VS2012 wprowadza Just My Code, który polecam również włączyć .
W przypadku debugowania z włączoną funkcją Just My Code zachowanie jest nieco inne. Po włączeniu Just My Code debugger ignoruje wyjątki środowiska uruchomieniowego CLR pierwszej klasy, które są wyrzucane poza Mój kod i nie przechodzą przez Mój kod
Simon Mourier podał ten przykład :
object o = null;
DateTime d = (DateTime)o; // NullReferenceException
gdzie unboxing konwersji (cast) z object
(lub z jednej z klas System.ValueType
lub System.Enum
czy od typu interfejsu) do typu wartości (inne niż Nullable<>
) sam w sobie dajeNullReferenceException
.
W drugim kierunku, boks konwersji zNullable<>
, który ma HasValue
równy false
z typu odniesienia, można podać null
odniesienie, który później może prowadzić do NullReferenceException
. Klasyczny przykład to:
DateTime? d = null;
var s = d.ToString(); // OK, no exception (no boxing), returns ""
var t = d.GetType(); // Bang! d is boxed, NullReferenceException
Czasami boks dzieje się w inny sposób. Na przykład za pomocą tej niestandardowej metody rozszerzenia:
public static void MyExtension(this object x)
{
x.ToString();
}
następujący 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 zasad, które środowisko wykonawcze stosuje podczas tworzenia boksu Nullable<>
.
Dodanie przypadku, gdy nazwa klasy dla encji użytej w strukturze encji jest taka sama jak nazwa klasy dla pliku internetowego z kodem.
Załóżmy, że masz formularz internetowy Contact.aspx, którego klasą kodebehind jest Kontakt i masz nazwę encji Kontakt.
Następnie następujący kod zgłosi wyjątek NullReferenceException podczas wywoływania kontekstu.SaveChanges ()
Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line
Ze względu na kompletność klasy DataContext
public class DataContext : DbContext
{
public DbSet<Contact> Contacts {get; set;}
}
i klasa jednostki kontaktowej. Czasami klasy jednostek są częściowymi klasami, dzięki czemu można je również rozszerzyć na inne pliki.
public partial class Contact
{
public string Name {get; set;}
}
Błąd występuje, gdy zarówno encja, jak i klasa codebehind znajdują się w tej samej przestrzeni nazw. Aby to naprawić, zmień nazwę klasy encji lub klasy codebehind na Contact.aspx.
Powód Nadal nie jestem pewien przyczyny. Ale ilekroć dowolna klasa jednostek rozszerzy System.Web.UI.Page, ten błąd występuje.
Do dyskusji spójrz na NullReferenceException w DbContext.saveChanges ()
Innym ogólnym przypadkiem, w którym można otrzymać ten wyjątek, są drwiny z klas podczas testów jednostkowych. Bez względu na stosowane ramy próbne należy upewnić się, że wszystkie odpowiednie poziomy hierarchii klas są odpowiednio wyśmiewane. W szczególności wszystkie właściwości, do HttpContext
których odwołuje się testowany kod, muszą zostać wyszydzone.
Zobacz „ NullReferenceException zgłaszany podczas testowania niestandardowego atrybutu AuthorizationAttribute ”, aby uzyskać nieco pełny przykład.
Mam inne spojrzenie na odpowiedź na to pytanie. Tego rodzaju odpowiedzi „co jeszcze mogę zrobić, aby tego uniknąć? ”
Podczas pracy na różnych warstwach , na przykład w aplikacji MVC, kontroler potrzebuje usług do wywoływania operacji biznesowych. W takich scenariuszach można użyć kontenera wstrzykiwania zależności do zainicjowania usług, aby uniknąć wyjątku NullReferenceException . Oznacza to, że nie musisz się martwić sprawdzaniem wartości zerowej i po prostu wywołujesz usługi z kontrolera, tak jakby zawsze były dostępne (i inicjowane) jako singleton lub prototyp.
public class MyController
{
private ServiceA serviceA;
private ServiceB serviceB;
public MyController(ServiceA serviceA, ServiceB serviceB)
{
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void MyMethod()
{
// We don't need to check null because the dependency injection container
// injects it, provided you took care of bootstrapping it.
var someObject = serviceA.DoThis();
}
}
W kwestii „co mam z tym zrobić” może być wiele odpowiedzi.
Bardziej „formalnym” sposobem zapobiegania takim błędom podczas opracowywania jest stosowanie projektu w umowie w kodzie. Oznacza to, że trzeba ustawić klasy niezmienników i / lub nawet funkcja / metoda warunki i postconditions na komputerze, podczas gdy rozwija.
Krótko mówiąc, niezmienniki klasowe zapewniają, że będą pewne ograniczenia w klasie, które nie zostaną naruszone podczas normalnego użytkowania (a zatem klasa nie przejdzie w niespójny stan). Warunki oznaczają, że dane podane jako wejście do funkcji / metody musi podążać ustawić pewne ograniczenia i nigdy ich naruszać, a postconditions oznacza, że wyjście funkcja / metoda musi przestrzegać ustalonych ograniczeń ponownie bez kiedykolwiek ich naruszenie. Warunki umowy nie powinny być nigdy naruszane podczas wykonywania programu wolnego od błędów, dlatego projektowanie według umowy jest sprawdzane w praktyce w trybie debugowania, podczas gdy jest wyłączone w wydaniach , aby zmaksymalizować wydajność opracowanego systemu.
W ten sposób można uniknąć NullReferenceException
przypadków, które są wynikiem naruszenia zestawu ograniczeń. Na przykład, jeśli użyjesz właściwości obiektu X
w klasie, a później spróbujesz wywołać jedną z jej metod i X
ma ona wartość zerową, spowoduje to NullReferenceException
:
public X { get; set; }
public void InvokeX()
{
X.DoSomething(); // if X value is null, you will get a NullReferenceException
}
Jeśli jednak ustawisz „właściwość X nigdy nie może mieć wartości zerowej” jako warunek wstępny metody, możesz zapobiec scenariuszowi opisanemu wcześniej:
//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant ()
{
Contract.Invariant ( X != null );
//...
}
Z tego powodu istnieje projekt Code Contracts dla aplikacji .NET.
Alternatywnie, projekt na podstawie umowy może być zastosowany przy użyciu asercji .
AKTUALIZACJA: Warto wspomnieć, że termin został ukuty przez Bertranda Meyera w związku z jego projektem języka programowania Eiffel .
A NullReferenceException
jest wyrzucany, gdy próbujemy uzyskać dostęp do właściwości obiektu zerowego lub gdy wartość ciągu staje się pusta i próbujemy uzyskać dostęp do metod ciągu.
Na przykład:
Gdy uzyskano dostęp do metody ciągu pustego:
string str = string.Empty;
str.ToLower(); // throw null reference exception
Gdy uzyskano dostęp do właściwości pustego obiektu:
Public Class Person {
public string Name { get; set; }
}
Person objPerson;
objPerson.Name /// throw Null refernce Exception
String.Empty.ToLower()
nie zgłosi wyjątku zerowego odniesienia. Reprezentuje rzeczywisty ciąg, choć pusty (tj ""
.). Ponieważ ma to obiekt do wywołania ToLower()
, nie ma sensu umieszczać w nim wyjątku o wartości zerowej.
TL; DR: Spróbuj użyć Html.Partial
zamiastRenderpage
Otrzymywałem, Object reference not set to an instance of an object
gdy próbowałem renderować widok w widoku, wysyłając mu model, taki jak ten:
@{
MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
Debugowanie pokazało, że model był pusty w MyOtherView. Aż zmieniłem to na:
@{
MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);
I zadziałało.
Ponadto powodem, dla którego nie musiałem Html.Partial
zaczynać, było to, że Visual Studio czasami rzuca wyglądające na błędne linie, pod którymi Html.Partial
znajduje się w inaczej skonstruowanej foreach
pętli, mimo że tak naprawdę nie jest to błąd:
@inherits System.Web.Mvc.WebViewPage
@{
ViewBag.Title = "Entity Index";
List<MyEntity> MyEntities = new List<MyEntity>();
MyEntities.Add(new MyEntity());
MyEntities.Add(new MyEntity());
MyEntities.Add(new MyEntity());
}
<div>
@{
foreach(var M in MyEntities)
{
// Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
@Html.Partial("MyOtherView.cshtml");
}
}
</div>
Ale udało mi się uruchomić aplikację bez problemów z tym „błędem”. Mogłem pozbyć się błędu, zmieniając strukturę foreach
pętli, aby wyglądała następująco:
@foreach(var M in MyEntities){
...
}
Chociaż mam wrażenie, że było tak, ponieważ Visual Studio źle odczytał znaki handlowe i nawiasy klamrowe.
Html.Partial
, nie@Html.Partial
Null
), więc wiedziałem, że błąd dotyczy sposobu wysyłania modelu.
Co możesz z tym zrobić?
Istnieje wiele dobrych odpowiedzi wyjaśniających, czym jest odwołanie zerowe i jak go debugować. Ale jest bardzo niewiele, jak zapobiec problemowi lub przynajmniej ułatwić złapanie.
Sprawdź argumenty
Na przykład metody mogą sprawdzić różne argumenty, aby sprawdzić, czy są one puste, i wygenerować ArgumentNullException
wyjątek, który oczywiście został stworzony w tym właśnie celu.
Konstruktor ArgumentNullException
parzystego przyjmuje nazwę parametru i komunikat jako argumenty, abyś mógł powiedzieć deweloperowi, na czym polega problem.
public void DoSomething(MyObject obj) {
if(obj == null)
{
throw new ArgumentNullException("obj", "Need a reference to obj.");
}
}
Użyj narzędzi
Istnieje również kilka bibliotek, które mogą pomóc. Na przykład „Resharper” może dostarczyć ostrzeżeń podczas pisania kodu, zwłaszcza jeśli użyjesz ich atrybutu: NotNullAttribute
Istnieje „Microsoft Code Contracts”, w których używasz składni, Contract.Requires(obj != null)
która zapewnia sprawdzanie czasu wykonywania i kompilacji: Wprowadzenie umów Code .
Istnieje również „PostSharp”, który pozwoli ci używać takich atrybutów:
public void DoSometing([NotNull] obj)
W ten sposób i włączenie PostSharp do procesu kompilacji obj
będzie sprawdzane pod kątem zerowości w czasie wykonywania. Zobacz: Kontrola zerowa PostSharp
Rozwiązanie Plain Code
Lub zawsze możesz zakodować własne podejście, używając zwykłego starego kodu. Na przykład tutaj jest struktura, której można użyć do przechwytywania zerowych referencji. Jest wzorowany na tej samej koncepcji, co Nullable<T>
:
[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
private T _value;
public T Value
{
get
{
if (_value == null)
{
throw new Exception("null value not allowed");
}
return _value;
}
set
{
if (value == null)
{
throw new Exception("null value not allowed.");
}
_value = value;
}
}
public static implicit operator T(NotNull<T> notNullValue)
{
return notNullValue.Value;
}
public static implicit operator NotNull<T>(T value)
{
return new NotNull<T> { Value = value };
}
}
Użyłbyś bardzo podobnego do tego samego, którego używałbyś Nullable<T>
, z wyjątkiem celu osiągnięcia dokładnie odwrotnej sytuacji - aby nie pozwolić null
. Oto kilka przykładów:
NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null
NotNull<T>
jest niejawnie przesyłany do iz miejsca, T
dzięki czemu można go używać niemal wszędzie tam, gdzie jest to potrzebne. Na przykład możesz przekazać Person
obiekt do metody, która przyjmuje NotNull<Person>
:
Person person = new Person { Name = "John" };
WriteName(person);
public static void WriteName(NotNull<Person> person)
{
Console.WriteLine(person.Value.Name);
}
Jak widać powyżej, podobnie jak w przypadku wartości zerowej, można uzyskać dostęp do wartości bazowej za pośrednictwem Value
właściwości. Alternatywnie możesz użyć jawnej lub niejawnej rzutowania, możesz zobaczyć przykład z wartością zwracaną poniżej:
Person person = GetPerson();
public static NotNull<Person> GetPerson()
{
return new Person { Name = "John" };
}
Możesz też użyć go, gdy metoda właśnie powraca T
(w tym przypadku Person
), wykonując rzut. Na przykład poniższy kod byłby podobny do powyższego:
Person person = (NotNull<Person>)GetPerson();
public static Person GetPerson()
{
return new Person { Name = "John" };
}
Połącz z rozszerzeniem
W połączeniu NotNull<T>
z metodą rozszerzenia możesz objąć jeszcze więcej sytuacji. Oto przykład, jak może wyglądać metoda rozszerzenia:
[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
public static T NotNull<T>(this T @this) where T: class
{
if (@this == null)
{
throw new Exception("null value not allowed");
}
return @this;
}
}
A oto przykład, jak można go użyć:
var person = GetPerson().NotNull();
GitHub
W celach informacyjnych udostępniłem powyższy kod na GitHub, można go znaleźć pod adresem:
https://github.com/luisperezphd/NotNull
Powiązana funkcja języka
W C # 6.0 wprowadzono „operator null-warunkowy”, który trochę w tym pomaga. Dzięki tej funkcji możesz odwoływać się do zagnieżdżonych obiektów i jeśli którykolwiek z nich null
zwraca całe wyrażenie null
.
Zmniejsza to liczbę kontroli zerowych, które musisz wykonać w niektórych przypadkach. Składnia polega na umieszczeniu znaku zapytania przed każdą kropką. Weźmy na przykład następujący kod:
var address = country?.State?.County?.City;
Wyobraź sobie, że country
jest to obiekt typu, Country
który ma właściwość o nazwie State
i tak dalej. Jeśli country
, State
, County
, lub City
jest null
wtedy address will be
zerowy . Therefore you only have to check whether
adresis
null`.
To świetna funkcja, ale daje mniej informacji. Nie jest oczywiste, który z 4 jest pusty.
Wbudowany jak Nullable?
C # ma ładny skrót Nullable<T>
, możesz zrobić coś null, umieszczając znak zapytania za takim typem int?
.
Byłoby miło, gdyby C # miał coś w rodzaju NotNull<T>
struktury powyżej miał podobny skrót, może wykrzyknik tak, że można napisać coś takiego (!) public void WriteName(Person! person)
.
Co ciekawe, żadna z odpowiedzi na tej stronie nie wspomina o dwóch przypadkach, mam nadzieję, że nikt nie będzie miał nic przeciwko, jeśli je dodam:
Słowniki ogólne w .NET nie są bezpieczne dla wątków i czasami mogą rzucać a, NullReference
a nawet (częściej), KeyNotFoundException
gdy próbujesz uzyskać dostęp do klucza z dwóch współbieżnych wątków. Wyjątek jest w tym przypadku dość mylący.
Jeśli kod NullReferenceException
jest rzucany przez a unsafe
, możesz spojrzeć na zmienne wskaźnika i sprawdzić, IntPtr.Zero
czy nie. Co jest tym samym („wyjątek wskaźnika zerowego”), ale w niebezpiecznym kodzie zmienne są często rzutowane na typy wartości / tablice itp., A ty uderzasz głową o ścianę, zastanawiając się, jak typ wartości może to rzucić wyjątek.
(Nawiasem mówiąc, kolejny powód nieużywania niebezpiecznego kodu, chyba że jest on potrzebny)
null
w jaki sposób?
Możesz naprawić NullReferenceException w czysty sposób za pomocą Null-warunkowych operatorów w c # 6 i napisać mniej kodu do obsługi kontroli zerowej.
Służy do testowania wartości zerowej przed wykonaniem operacji dostępu do elementu (?.) Lub indeksu (? [).
Przykład
var name = p?.Spouse?.FirstName;
jest równa:
if (p != null)
{
if (p.Spouse != null)
{
name = p.Spouse.FirstName;
}
}
Rezultat jest taki, że nazwa będzie pusta, gdy p jest puste lub gdy p. Małżonek jest pusty.
W przeciwnym razie do nazwy zmiennej zostanie przypisana wartość p.Spouse.FirstName.
Aby uzyskać więcej informacji: Operatory warunkowe o wartości NULL
Wiersz błędu „Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu.” Stwierdza, że nie przypisałeś obiektu wystąpienia do odwołania do obiektu i nadal masz dostęp do właściwości / metod tego obiektu.
na przykład: załóżmy, że masz klasę o nazwie myClass i zawiera ona jedną właściwość prop1.
public Class myClass
{
public int prop1 {get;set;}
}
Teraz uzyskujesz dostęp do tego prop1 w innej klasie, tak jak poniżej:
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref.prop1 = 1; //This line throws error
}
}
powyższa linia zgłasza błąd, ponieważ zadeklarowano odwołanie do klasy myClass, ale nie zostało utworzone lub instancja obiektu nie jest przypisana do referencji tej klasy.
Aby to naprawić, musisz utworzyć instancję (przypisać obiekt do odwołania do tej klasy).
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref = new myClass();
ref.prop1 = 1;
}
}
NullReferenceException lub odwołanie do obiektu nie ustawione na instancję obiektu występuje, gdy obiekt klasy, z której próbujesz skorzystać, nie jest tworzony. Na przykład:
Załóżmy, że masz klasę o nazwie Student.
public class Student
{
private string FirstName;
private string LastName;
public string GetFullName()
{
return FirstName + LastName;
}
}
Teraz rozważ inną klasę, w której próbujesz odzyskać pełne imię i nazwisko ucznia.
public class StudentInfo
{
public string GetStudentName()
{
Student s;
string fullname = s.GetFullName();
return fullname;
}
}
Jak widać w powyższym kodzie, instrukcja Student - deklaruje tylko zmienną typu Student, zauważ, że klasa Student nie jest w tym momencie tworzona. Dlatego gdy zostanie wykonana instrukcja s.GetFullName (), zgłosi wyjątek NullReferenceException.
Cóż, w prostych słowach:
Próbujesz uzyskać dostęp do obiektu, który nie został utworzony lub nie znajduje się w pamięci.
Jak więc rozwiązać ten problem:
Debuguj i pozwól, aby debugger się zepsuł ... To zabierze Cię bezpośrednio do zmiennej, która jest zepsuta ... Teraz Twoim zadaniem jest po prostu to naprawić .. Używając nowego słowa kluczowego w odpowiednim miejscu.
Jeśli jest to spowodowane niektórymi poleceniami bazy danych , ponieważ obiekt nie jest obecny, wszystko, co musisz zrobić, to sprawdzić zerowo i obsłużyć go:
if (i == null) {
// Handle this
}
Najtrudniejszy ... jeśli GC już zebrał obiekt ... Z reguły dzieje się tak, jeśli próbujesz znaleźć obiekt za pomocą ciągów ... Znalezienie go po nazwie obiektu może się zdarzyć, że GC może już wyczyściłem go ... Jest to trudne do znalezienia i stanie się sporym problemem ... Lepszym sposobem na rozwiązanie tego problemu jest przeprowadzanie zerowych kontroli tam, gdzie jest to konieczne podczas procesu programowania. Zaoszczędzi ci to dużo czasu.
Przez znajdowanie po nazwie rozumiem, że niektóre ramy pozwalają FIndObjects za pomocą ciągów, a kod może wyglądać następująco: FindObject („ObjectName”);
Dosłownie najłatwiejszy sposób na naprawienie wyjątku NullReferenceException ma dwa sposoby. Jeśli masz GameObject, na przykład z dołączonym skryptem i zmienną o nazwie rb (rigidbody), ta zmienna zacznie być zerowa po uruchomieniu gry.
To dlatego otrzymujesz NullReferenceException, ponieważ komputer nie ma danych przechowywanych w tej zmiennej.
Będę używał zmiennej RigidBody jako przykładu.
Możemy dodawać dane naprawdę łatwo na kilka sposobów:
rb = GetComponent<Rigidbody>();
Start()
lub Awake()
funkcjami. rb = AddComponent<RigidBody>();
Dalsze uwagi: Jeśli chcesz, aby jedność [RequireComponent(typeof(RigidBody))]
dodała komponent do twojego obiektu i mogłeś zapomnieć o dodaniu jednego, możesz wpisać powyżej deklaracji klasy (miejsce poniżej wszystkich twoich zastosowań).
Ciesz się i baw się dobrze tworząc gry!
Jeśli weźmiemy pod uwagę typowe scenariusze, w których ten wyjątek może zostać zgłoszony, uzyskujemy dostęp do właściwości z obiektem na górze.
Dawny:
string postalcode=Customer.Address.PostalCode;
//if customer or address is null , this will through exeption
tutaj, jeśli adres ma wartość NULL, otrzymasz NullReferenceException.
Tak więc, jako praktyka, powinniśmy zawsze używać sprawdzania wartości zerowej, zanim uzyskamy dostęp do właściwości w takich obiektach (szczególnie w ogólności)
string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
Jest to w zasadzie wyjątek odniesienia Null . Jak Microsoft Zjednoczonych byli
Wyjątek NullReferenceException jest zgłaszany, gdy próbujesz uzyskać dostęp do elementu typu, którego wartość jest równa null.
Oznacza to, że jeśli jakikolwiek członek, który nie ma żadnej wartości, a my zmuszamy tego członka do wykonania określonego zadania, to system bez wątpienia wyrzuci wiadomość i powie:
„Hej, czekaj, ten członek nie ma wartości, więc nie może wykonać zadania, które mu przekazujesz”.
Sam wyjątek mówi, że coś jest odsyłane, ale którego wartość nie jest ustawiana. Oznacza to, że występuje tylko podczas używania typów odwołań, ponieważ typów wartości nie można zerować.
NullReferenceException nie wystąpi, jeśli używamy elementów typu Value.
class Program
{
static void Main(string[] args)
{
string str = null;
Console.WriteLine(str.Length);
Console.ReadLine();
}
}
Powyższy kod pokazuje prosty ciąg znaków, któremu przypisano null wartość .
Teraz, gdy próbuję wydrukować długość łańcucha str , pojawia się komunikat Nieobsługiwany wyjątek typu „System.NullReferenceException”, ponieważ element str pojawia się wskazuje na null i nie może być żadnej długości null.
„ NullReferenceException ” występuje również wtedy, gdy zapomnimy utworzyć instancję typu odwołania.
Załóżmy, że mam w nim metodę klasy i członka. Nie utworzyłem instancji mojej klasy, a jedynie jej nazwę. Teraz, jeśli spróbuję użyć tej metody, kompilator wyśle błąd lub wygeneruje ostrzeżenie (w zależności od kompilatora).
class Program
{
static void Main(string[] args)
{
MyClass1 obj;
obj.foo(); //Use of unassigned local variable 'obj'
}
}
public class MyClass1
{
internal void foo()
{
Console.WriteLine("hello from foo");
}
}
Kompilator powyższego kodu generuje błąd polegający na tym, że zmienna obj nie jest przypisana, co oznacza, że nasza zmienna ma wartości zerowe lub nie ma jej wcale. Kompilator powyższego kodu generuje błąd polegający na tym, że zmienna obj nie jest przypisana, co oznacza, że nasza zmienna ma wartości zerowe lub nie ma jej wcale.
Wyjątek NullReferenceException powstaje z powodu naszej winy za nie sprawdzenie wartości obiektu. Często pozostawiamy wartości obiektów niezaznaczone w rozwoju kodu.
Powstaje również, gdy zapomnimy utworzyć instancję naszych obiektów. Przyczyną tego wyjątku może być także użycie metod, właściwości, kolekcji itp., Które mogą zwracać lub ustawiać wartości zerowe.
Istnieją różne sposoby i metody unikania tego znanego wyjątku:
Jawne sprawdzanie: powinniśmy przestrzegać tradycji sprawdzania obiektów, właściwości, metod, tablic i kolekcji, czy są one zerowe. Można to po prostu zaimplementować za pomocą instrukcji warunkowych, takich jak if-else if-else itp.
Obsługa wyjątków: Jeden z ważnych sposobów zarządzania tym wyjątkiem. Za pomocą prostych bloków try-catch-wreszcie możemy kontrolować ten wyjątek, a także prowadzić jego rejestr. Może to być bardzo przydatne, gdy aplikacja jest na etapie produkcji.
Operatory zerowe: Operator koalescencji zerowej i operatory warunkowe zerowe mogą być również przydatne podczas ustawiania wartości obiektów, zmiennych, właściwości i pól.
Debuger: dla programistów mamy wielką broń podczas debugowania. Jeśli napotkamy NullReferenceException podczas obliczeń programistycznych, możemy użyć debuggera, aby dostać się do źródła wyjątku.
Metoda wbudowana: metody systemowe, takie jak GetValueOrDefault (), IsNullOrWhiteSpace () i IsNullorEmpty () sprawdzają wartości null i przypisują wartość domyślną, jeśli istnieje wartość null.
Istnieje już wiele dobrych odpowiedzi. Możesz także sprawdzić bardziej szczegółowy opis z przykładami na moim blogu .
Mam nadzieję, że to też pomaga!
Jeśli otrzymujesz ten komunikat podczas zapisywania lub kompilacji kompilacji, po prostu zamknij wszystkie pliki, a następnie otwórz dowolny plik, aby skompilować i zapisać.
Dla mnie powodem było to, że zmieniłem nazwę pliku i stary plik był nadal otwarty.