Ich habe einen Code und wenn er ausgeführt wird, wirft er ein NullReferenceException
und sagt:
Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.
Was bedeutet das und was kann ich tun, um diesen Fehler zu beheben?
Ich habe einen Code und wenn er ausgeführt wird, wirft er ein NullReferenceException
und sagt:
Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.
Was bedeutet das und was kann ich tun, um diesen Fehler zu beheben?
Antworten:
Sie versuchen, etwas zu verwenden, das null
(oder Nothing
in VB.NET) ist. Dies bedeutet, dass Sie es entweder auf null
oder gar nichts einstellen.
Wie alles andere wird null
herumgereicht. Wenn es null
in Verfahren „A“, könnte es diese Methode „B“ sein , eine übergeben null
zu Verfahren „A“.
null
kann verschiedene Bedeutungen haben:
NullReferenceException
.null
absichtlich, um anzuzeigen, dass kein aussagekräftiger Wert verfügbar ist. Beachten Sie, dass C # das Konzept nullbarer Datentypen für Variablen hat (wie Datenbanktabellen nullfähige Felder haben können) - Sie können null
ihnen zuweisen , um anzuzeigen, dass kein Wert darin gespeichert ist, z. B. int? a = null;
wenn das Fragezeichen angibt, dass null gespeichert werden darf variabel a
. Sie können dies entweder mit if (a.HasValue) {...}
oder mit überprüfen if (a==null) {...}
. Nullable Variablen, wie in a
diesem Beispiel, ermöglichen den a.Value
expliziten Zugriff auf den Wert über oder wie gewohnt über a
. a.Value
wirft einen InvalidOperationException
statt eines , NullReferenceException
wenn a
ISnull
- Sie sollten die Prüfung im Voraus durchführen. Wenn Sie also eine andere Variable mit Nullwert haben, int b;
sollten Sie Zuweisungen wie if (a.HasValue) { b = a.Value; }
oder kürzer ausführen if (a != null) { b = a; }
.Der Rest dieses Artikels geht detaillierter und zeigt Fehler, die viele Programmierer häufig machen und die zu a führen können NullReferenceException
.
Das runtime
Werfen eines bedeutet NullReferenceException
immer dasselbe: Sie versuchen, eine Referenz zu verwenden, und die Referenz wird nicht initialisiert (oder sie wurde einmal initialisiert, wird aber nicht mehr initialisiert).
Dies bedeutet, dass die Referenz lautet null
und Sie nicht über eine null
Referenz auf Mitglieder (z. B. Methoden) zugreifen können . Der einfachste Fall:
string foo = null;
foo.ToUpper();
Dies wirft ein a NullReferenceException
in die zweite Zeile, da Sie die Instanzmethode ToUpper()
für eine string
Referenz, auf die verweist, nicht aufrufen können null
.
Wie finden Sie die Quelle von a NullReferenceException
? Abgesehen von der Betrachtung der Ausnahme selbst, die genau an der Stelle ausgelöst wird, an der sie auftritt, gelten die allgemeinen Regeln für das Debuggen in Visual Studio: Platzieren Sie strategische Haltepunkte und überprüfen Sie Ihre Variablen , indem Sie entweder mit der Maus über deren Namen fahren und ein (öffnen) Schnell) Beobachten Sie das Fenster oder verwenden Sie die verschiedenen Debugging-Bereiche wie Locals und Autos.
Wenn Sie herausfinden möchten, wo die Referenz festgelegt ist oder nicht, klicken Sie mit der rechten Maustaste auf den Namen und wählen Sie "Alle Referenzen suchen". Sie können dann an jedem gefundenen Speicherort einen Haltepunkt platzieren und Ihr Programm mit dem angehängten Debugger ausführen. Jedes Mal, wenn der Debugger an einem solchen Haltepunkt unterbrochen wird, müssen Sie feststellen, ob die Referenz nicht null sein soll, die Variable überprüfen und sicherstellen, dass sie auf eine Instanz verweist, wenn Sie dies erwarten.
Wenn Sie dem Programmablauf auf diese Weise folgen, können Sie den Speicherort ermitteln, an dem die Instanz nicht null sein sollte und warum sie nicht richtig festgelegt ist.
Einige häufige Szenarien, in denen die Ausnahme ausgelöst werden kann:
ref1.ref2.ref3.member
Wenn ref1 oder ref2 oder ref3 null ist, erhalten Sie eine NullReferenceException
. Wenn Sie das Problem lösen möchten, finden Sie heraus, welches Null ist, indem Sie den Ausdruck in sein einfacheres Äquivalent umschreiben:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
Insbesondere in HttpContext.Current.User.Identity.Name
der HttpContext.Current
könnte null sein, oder die User
Eigenschaft null sein könnte, oder die Identity
Eigenschaft null sein könnte.
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.
}
}
Wenn Sie die untergeordnete Nullreferenz (Person) vermeiden möchten, können Sie sie im Konstruktor des übergeordneten (Buch-) Objekts initialisieren.
Gleiches gilt für verschachtelte Objektinitialisierer:
Book b1 = new Book
{
Author = { Age = 45 }
};
Dies bedeutet
Book b1 = new Book();
b1.Author.Age = 45;
Während das new
Schlüsselwort verwendet wird, wird nur eine neue Instanz von erstellt Book
, jedoch keine neue Instanz von Person
, sodass Author
die Eigenschaft weiterhin vorhanden ist null
.
public class Person
{
public ICollection<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
Die verschachtelte Auflistung Initializers
verhält sich wie folgt:
Person p1 = new Person
{
Books = {
new Book { Title = "Title1" },
new Book { Title = "Title2" },
}
};
Dies bedeutet
Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });
Das new Person
erstellt nur eine Instanz von Person
, aber die Books
Sammlung ist noch null
. Die Auflistungssyntax erstellt Initializer
keine Auflistung für p1.Books
, sondern übersetzt nur in die p1.Books.Add(...)
Anweisungen.
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.
öffentliche Klasse Form1 {Privatkunde Kunde;
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);
}
}}
Dies kann gelöst werden, indem Sie der Konvention folgen, um Feldern einen Unterstrich voranzustellen:
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();
Wenn die Ausnahme auftritt, wenn auf eine Eigenschaft von @Model
in verwiesen wird ASP.NET MVC View
, müssen Sie verstehen, dass die Model
in Ihrer Aktionsmethode festgelegt wird, wenn Sie return
eine Ansicht erstellen. Wenn Sie ein leeres Modell (oder eine Modelleigenschaft) von Ihrem Controller zurückgeben, tritt die Ausnahme auf, wenn die Ansichten darauf zugreifen:
// 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
Steuerelemente werden während des Aufrufs InitializeComponent
in der Reihenfolge erstellt, in der sie im visuellen Baum angezeigt werden. Bei NullReferenceException
früh erstellten Steuerelementen mit Ereignishandlern usw. wird ein Wert ausgelöst, bei InitializeComponent
dem spät erstellte Steuerelemente referenziert werden.
Beispielsweise :
<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>
Hier comboBox1
wird vorher erstellt label1
. Wenn comboBox1_SelectionChanged
versucht wird, auf `label1 zu verweisen, wurde es noch nicht erstellt.
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}
Eine Änderung der Reihenfolge der Deklarationen in der XAML
(dh Auflistung label1
vor comboBox1
, Ignorieren von Fragen der Designphilosophie, würde zumindest das NullReferenceException
hier lösen .
as
var myThing = someObject as Thing;
Dies wirft kein, InvalidCastException
sondern gibt ein zurück, null
wenn die Umwandlung fehlschlägt (und wenn someObject
es selbst null ist). Also sei dir dessen bewusst.
FirstOrDefault()
undSingleOrDefault()
Die einfachen Versionen First()
und Single()
werfen Ausnahmen, wenn es nichts gibt. Die "OrDefault" -Versionen geben in diesem Fall null zurück. Also sei dir dessen bewusst.
foreach
wird ausgelöst, wenn Sie versuchen, die Nullsammlung zu iterieren. Wird normalerweise durch unerwartete null
Ergebnisse von Methoden verursacht, die Sammlungen zurückgeben.
List<int> list = null;
foreach(var v in list) { } // exception
Realistischeres Beispiel - Wählen Sie Knoten aus dem XML-Dokument aus. Wird ausgelöst, wenn keine Knoten gefunden werden, aber das anfängliche Debuggen zeigt, dass alle Eigenschaften gültig sind:
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
null
und ignorieren Sie explizit Nullwerte.Wenn Sie erwarten, dass die Referenz manchmal null ist, können Sie dies überprüfen, null
bevor Sie auf Instanzmitglieder zugreifen:
void PrintName(Person p)
{
if (p != null)
{
Console.WriteLine(p.Name);
}
}
null
einem Standardwert und geben Sie ihn an.Methodenaufruf, von dem Sie erwarten, dass er eine Instanz zurückgibt null
, kann zurückgegeben werden , beispielsweise wenn das gesuchte Objekt nicht gefunden werden kann. In diesem Fall können Sie einen Standardwert zurückgeben:
string GetCategory(Book b)
{
if (b == null)
return "Unknown";
return b.Category;
}
null
Methodenaufrufen und lösen Sie eine benutzerdefinierte Ausnahme aus.Sie können auch eine benutzerdefinierte Ausnahme auslösen, um sie im aufrufenden Code abzufangen:
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
diese Option, wenn ein Wert niemals sein sollte null
, um das Problem früher zu erkennen, als die Ausnahme auftritt.Wenn Sie während der Entwicklung wissen, dass eine Methode möglicherweise zurückkehren kann, aber niemals zurückkehren sollte null
, können Sie sie verwenden Debug.Assert()
, um so schnell wie möglich zu brechen, wenn sie auftritt:
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.
}
Diese Prüfung endet jedoch nicht in Ihrem Release-Build , sodass sie zur Laufzeit im Release-Modus NullReferenceException
erneut book == null
ausgelöst wird.
GetValueOrDefault()
für nullable
Werttypen, um einen Standardwert anzugeben, wenn dies der Fall ist 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 #] oder If()
[VB].Die Abkürzung zum Bereitstellen eines Standardwerts, wenn a null
auftritt:
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;
}
?.
oder ?[x]
für Arrays (verfügbar in C # 6 und VB.NET 14):Dies wird manchmal auch als sicherer Navigations- oder Elvis-Operator (nach seiner Form) bezeichnet. Wenn der Ausdruck auf der linken Seite des Operators null ist, wird die rechte Seite nicht ausgewertet und stattdessen null zurückgegeben. Das bedeutet Fälle wie diesen:
var title = person.Title.ToUpper();
Wenn die Person keinen Titel hat, wird eine Ausnahme ausgelöst, da versucht wird, ToUpper
eine Eigenschaft mit einem Nullwert aufzurufen .
In C# 5
und unten kann dies bewacht werden mit:
var title = person.Title == null ? null : person.Title.ToUpper();
Jetzt ist die Titelvariable null, anstatt eine Ausnahme auszulösen. C # 6 führt hierfür eine kürzere Syntax ein:
var title = person.Title?.ToUpper();
Dies führt dazu, dass die Titelvariable vorhanden ist null
und der Aufruf von ToUpper
nicht erfolgt, wenn dies der Fall person.Title
ist null
.
Natürlich müssen Sie immer noch nach title
null suchen oder den Nullbedingungsoperator zusammen mit dem Null-Koaleszenzoperator ( ??
) verwenden, um einen Standardwert anzugeben:
// 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;
Ebenso können Sie für Arrays ?[i]
Folgendes verwenden:
int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
Dies führt Folgendes aus: Wenn myIntArray
null ist, gibt der Ausdruck null zurück und Sie können ihn sicher überprüfen. Wenn es ein Array enthält, funktioniert es wie folgt:
elem = myIntArray[i];
und gibt das i<sup>th</sup>
Element zurück.
Die dort eingeführten C# 8
Nullkontexte und nullbaren Referenztypen führen eine statische Analyse von Variablen durch und geben eine Compilerwarnung aus, wenn ein Wert möglicherweise null sein kann oder auf null gesetzt wurde. Mit den nullbaren Referenztypen können Typen explizit null sein.
Der annullierbare Annotationskontext und der nullbare Warnkontext können für ein Projekt mithilfe des Nullable
Elements in Ihrer csproj
Datei festgelegt werden. Dieses Element konfiguriert, wie der Compiler die Nullfähigkeit von Typen interpretiert und welche Warnungen generiert werden. Gültige Einstellungen sind:
Ein nullbarer Referenztyp wird mit derselben Syntax wie nullbare Werttypen notiert: a ?
wird an den Typ der Variablen angehängt.
C#
unterstützt "Iteratorblöcke" (in einigen anderen gängigen Sprachen "Generatoren" genannt). Null-Dereferenzierungsausnahmen können aufgrund der verzögerten Ausführung in Iteratorblöcken besonders schwierig zu debuggen sein:
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) { ... }
Wenn whatever
Ergebnisse in null
dann MakeFrob
wird werfen. Nun könnte man denken, dass das Richtige das Folgende ist:
// 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();
}
Warum ist das falsch? Da der Iteratorblock eigentlich nicht laufen , bis die foreach
! Der Aufruf, GetFrobs
einfach ein Objekt zurückzugeben, das bei Iteration den Iteratorblock ausführt.
Wenn Sie eine Nullprüfung wie diese schreiben, verhindern Sie die Null-Dereferenzierung, aber Sie verschieben die Nullargument-Ausnahme an den Punkt der Iteration , nicht an den Punkt des Aufrufs , und das Debuggen ist sehr verwirrend .
Die richtige Lösung ist:
// 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();
}
Erstellen Sie also eine private Hilfsmethode mit der Iteratorblocklogik und eine öffentliche Oberflächenmethode, die die Nullprüfung durchführt und den Iterator zurückgibt. Wenn jetzt GetFrobs
aufgerufen wird, erfolgt die Nullprüfung sofort und wird dann GetFrobsForReal
ausgeführt, wenn die Sequenz wiederholt wird.
Wenn Sie die Referenzquelle für LINQ
Objekte untersuchen, werden Sie feststellen, dass diese Technik durchgehend verwendet wird. Das Schreiben ist etwas umständlicher, erleichtert jedoch das Debuggen von Nullitätsfehlern erheblich. Optimieren Sie Ihren Code für die Bequemlichkeit des Anrufers und nicht für die Bequemlichkeit des Autors .
C#
hat einen "unsicheren" Modus, der, wie der Name schon sagt, äußerst gefährlich ist, da die normalen Sicherheitsmechanismen, die Speichersicherheit und Typensicherheit bieten, nicht durchgesetzt werden. Sie sollten keinen unsicheren Code schreiben, es sei denn, Sie haben ein gründliches und tiefes Verständnis der Funktionsweise des Speichers .
Im unsicheren Modus sollten Sie zwei wichtige Fakten kennen:
Um zu verstehen, warum das so ist, ist es hilfreich zu verstehen, wie .NET überhaupt Null-Dereferenzierungsausnahmen erzeugt. (Diese Angaben gelten für .NET unter Windows. Andere Betriebssysteme verwenden ähnliche Mechanismen.)
Speicher wird in virtualisiert Windows
; Jeder Prozess erhält einen virtuellen Speicherplatz mit vielen "Seiten" Speicher, die vom Betriebssystem verfolgt werden. Auf jeder Speicherseite sind Flags gesetzt, die bestimmen, wie sie verwendet werden dürfen: Lesen, Schreiben, Ausführen usw. Die unterste Seite ist als "Fehler erzeugen, wenn sie jemals in irgendeiner Weise verwendet wurde" markiert.
Sowohl ein Nullzeiger als auch eine Nullreferenz in C#
werden intern als die Zahl Null dargestellt. Daher führt jeder Versuch, sie in den entsprechenden Speicher zu dereferenzieren, dazu, dass das Betriebssystem einen Fehler erzeugt. Die .NET-Laufzeit erkennt diesen Fehler und verwandelt ihn in die Null-Dereferenzierungsausnahme.
Aus diesem Grund führt die Dereferenzierung sowohl eines Nullzeigers als auch einer Nullreferenz zu derselben Ausnahme.
Was ist mit dem zweiten Punkt? Das Dereferenzieren eines ungültigen Zeigers, der auf die unterste Seite des virtuellen Speichers fällt, verursacht denselben Betriebssystemfehler und damit dieselbe Ausnahme.
Warum macht das Sinn? Angenommen, wir haben eine Struktur mit zwei Ints und einen nicht verwalteten Zeiger gleich null. Wenn wir versuchen, das zweite int in der Struktur zu dereferenzieren, CLR
wird nicht versucht, auf den Speicher an Position Null zuzugreifen. Es wird auf den Speicher an Position vier zugreifen. Aber logischerweise ist dies eine Null-Dereferenzierung, da wir über die Null zu dieser Adresse gelangen .
Wenn Sie mit unsicherem Code arbeiten und eine Null-Dereferenzierungsausnahme erhalten, beachten Sie nur, dass der fehlerhafte Zeiger nicht null sein muss. Es kann sich um eine beliebige Stelle auf der untersten Seite handeln, und diese Ausnahme wird erzeugt.
new Book { Author = { Age = 45 } };
Wie funktioniert die innere Initialisierung überhaupt ... Ich kann mir keine Situation vorstellen, in der die innere Initialisierung jemals funktionieren würde, aber sie kompiliert und Intellisense funktioniert ... Es sei denn für Strukturen?
Das NullReference Exception
für Visual Basic unterscheidet sich nicht von dem in C # . Schließlich melden beide dieselbe Ausnahme, die in .NET Framework definiert ist, das sie beide verwenden. Ursachen, die nur für Visual Basic gelten, sind selten (möglicherweise nur eine).
Diese Antwort verwendet Visual Basic-Begriffe, -Syntax und -Kontext. Die verwendeten Beispiele stammen aus einer großen Anzahl früherer Fragen zum Stapelüberlauf. Dies ist Relevanz zu maximieren , indem die Verwendung von Arten von Situationen oft in Beiträgen gesehen. Ein bisschen mehr Erklärung wird auch für diejenigen gegeben, die es brauchen könnten. Ein ähnliches Beispiel wie Ihr ist hier sehr wahrscheinlich aufgeführt.
Hinweis:
NullReferenceException
(NRE) verursacht, wie man es findet, wie man es behebt und wie man es vermeidet. Ein NRE kann auf viele Arten verursacht werden, daher ist es unwahrscheinlich, dass dies Ihre einzige Begegnung ist.Die Meldung "Objekt nicht auf eine Instanz von Objekt festgelegt" bedeutet, dass Sie versuchen, ein Objekt zu verwenden, das nicht initialisiert wurde. Dies läuft auf eines davon hinaus:
Da das Problem eine Objektreferenz ist Nothing
, besteht die Antwort darin, sie zu untersuchen, um herauszufinden, welche. Stellen Sie dann fest, warum es nicht initialisiert wurde. Halten Sie die Maus über die verschiedenen Variablen und Visual Studio (VS) zeigt ihre Werte an - der Schuldige ist Nothing
.
Sie sollten auch alle Try / Catch-Blöcke aus dem entsprechenden Code entfernen, insbesondere solche, bei denen der Catch-Block nichts enthält. Dies führt dazu, dass Ihr Code abstürzt, wenn er versucht, ein Objekt zu verwenden Nothing
. Dies ist das, was Sie möchten, da es den genauen Ort des Problems identifiziert und es Ihnen ermöglicht, das Objekt zu identifizieren, das es verursacht.
Ein MsgBox
angezeigter Catch Error while...
hilft wenig. Diese Methode führt auch zu sehr schlechten Fragen zum Stapelüberlauf, da Sie die tatsächliche Ausnahme, das betroffene Objekt oder sogar die Codezeile, in der sie auftritt, nicht beschreiben können.
Sie können Ihre Objekte auch mit Locals Window
( Debug -> Windows -> Locals ) untersuchen.
Sobald Sie wissen, was und wo das Problem liegt, ist es normalerweise recht einfach zu beheben und schneller als das Posten einer neuen Frage.
Siehe auch:
Dim reg As CashRegister
...
TextBox1.Text = reg.Amount ' NRE
Das Problem ist, dass Dim
kein CashRegister- Objekt erstellt wird . Es wird nur eine Variable mit diesem Namen deklariert reg
. Das Deklarieren einer Objektvariablen und das Erstellen einer Instanz sind zwei verschiedene Dinge.
Abhilfe
Der New
Operator kann häufig verwendet werden, um die Instanz zu erstellen, wenn Sie sie deklarieren:
Dim reg As New CashRegister ' [New] creates instance, invokes the constructor
' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister
Wenn es nur angebracht ist, die Instanz später zu erstellen:
Private reg As CashRegister ' Declare
...
reg = New CashRegister() ' Create instance
Hinweis: Sie nicht verwenden , Dim
wieder in einem Verfahren, einschließlich dem Konstruktor ( Sub New
):
Private reg As CashRegister
'...
Public Sub New()
'...
Dim reg As New CashRegister
End Sub
Dadurch wird eine lokale Variable erstellt, reg
die nur in diesem Kontext (Sub) vorhanden ist. Die reg
Variable mit Modulebene, Scope
die Sie überall sonst verwenden, bleibt erhalten Nothing
.
Das Fehlen des
New
Operators ist die häufigste Ursache fürNullReference Exceptions
die in den Stapelüberlauffragen behandelten Fragen.Visual Basic versucht, den Prozess wiederholt zu verdeutlichen, indem es Folgendes verwendet
New
: Mit demNew
Operator wird ein neues Objekt erstellt undSub New
der Konstruktor aufgerufen, in dem Ihr Objekt eine andere Initialisierung durchführen kann.
Um klar zu sein, deklariertDim
(oder Private
) nur eine Variable und ihre . Der Umfang der Variablen - ob sie für das gesamte Modul / die gesamte Klasse vorhanden ist oder für eine Prozedur lokal ist - wird dadurch bestimmt, wo sie deklariert wird. Definiert die Zugriffsebene, nicht den Bereich .Type
Private | Friend | Public
Weitere Informationen finden Sie unter:
Arrays müssen auch instanziiert werden:
Private arr as String()
Dieses Array wurde nur deklariert, nicht erstellt. Es gibt verschiedene Möglichkeiten, ein Array zu initialisieren:
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) {}
Hinweis: Ab VS 2010 sind beim Initialisieren eines lokalen Arrays mit einem Literal und Option Infer
die Elemente As <Type>
und New
optional:
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}
Der Datentyp und die Arraygröße werden aus den zugewiesenen Daten abgeleitet. Klasse / Modulebene Erklärungen erfordern nach wie vor As <Type>
mit Option Strict
:
Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}
Beispiel: Array von Klassenobjekten
Dim arrFoo(5) As Foo
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i).Bar = i * 10 ' Exception
Next
Das Array wurde erstellt, die darin enthaltenen Foo
Objekte jedoch nicht.
Abhilfe
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i) = New Foo() ' Create Foo instance
arrFoo(i).Bar = i * 10
Next
Die Verwendung eines List(Of T)
Testaments macht es ziemlich schwierig, ein Element ohne ein gültiges Objekt zu haben:
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
Weitere Informationen finden Sie unter:
.NET-Sammlungen (von denen es viele Varianten gibt - Listen, Wörterbuch usw.) müssen ebenfalls instanziiert oder erstellt werden.
Private myList As List(Of String)
..
myList.Add("ziggy") ' NullReference
Sie erhalten dieselbe Ausnahme aus demselben Grund - myList
wurde nur deklariert, aber keine Instanz erstellt. Das Mittel ist das gleiche:
myList = New List(Of String)
' Or create an instance when declared:
Private myList As New List(Of String)
Ein häufiges Versehen ist eine Klasse, die eine Sammlung verwendet 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
Jede Prozedur führt zu einer NRE, da sie barList
nur deklariert und nicht instanziiert wird. Durch das Erstellen einer Instanz von Foo
wird nicht auch eine Instanz des internen erstellt barList
. Möglicherweise war dies die Absicht, dies im Konstruktor zu tun:
Public Sub New ' Constructor
' Stuff to do when a new Foo is created...
barList = New List(Of Bar)
End Sub
Dies ist nach wie vor falsch:
Public Sub New()
' Creates another barList local to this procedure
Dim barList As New List(Of Bar)
End Sub
Weitere Informationen finden Sie unter List(Of T)
Klasse .
Arbeiten mit Datenbanken bieten viele Möglichkeiten für einen Nullreference , weil es viele Objekte (sein Command
, Connection
, Transaction
, Dataset
, DataTable
, DataRows
auf einmal ....) im Einsatz. Hinweis: Es spielt keine Rolle, welchen Datenprovider Sie verwenden - MySQL, SQL Server, OleDB usw. - die Konzepte sind dieselben.
Beispiel 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
Nach wie vor wurde das ds
Dataset-Objekt deklariert, es wurde jedoch nie eine Instanz erstellt. Das DataAdapter
wird ein vorhandenes füllen DataSet
, kein erstellt. In diesem Fall warnt Sie die IDE , da dies ds
eine lokale Variable ist : Dies könnte passieren:
Wenn con
der Compiler als Variable auf Modul- / Klassenebene deklariert wird, wie dies der Fall zu sein scheint, kann er nicht wissen, ob das Objekt durch eine vorgelagerte Prozedur erstellt wurde. Warnungen nicht ignorieren.
Abhilfe
Dim ds As New DataSet
Beispiel 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)
Ein Tippfehler ein Problem hier: Employees
vs Employee
. Es wurde kein DataTable
Name "Mitarbeiter" erstellt, daher wurde ein NullReferenceException
Ergebnis erzielt, bei dem versucht wurde, darauf zuzugreifen. Ein weiteres potenzielles Problem ist die Annahme, dass dies Items
der Fall sein wird, wenn SQL eine WHERE-Klausel enthält.
Abhilfe
Da hierfür eine Tabelle verwendet Tables(0)
wird , werden durch die Verwendung Rechtschreibfehler vermieden. Untersuchen Rows.Count
kann auch helfen:
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
ist eine Funktion, die die Anzahl der Rows
Betroffenen zurückgibt, die auch getestet werden kann:
If da.Fill(ds, "Employees") > 0 Then...
Beispiel 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
Das DataAdapter
wird TableNames
wie im vorherigen Beispiel gezeigt bereitgestellt, analysiert jedoch keine Namen aus der SQL- oder Datenbanktabelle. Als Ergebnis ds.Tables("TICKET_RESERVATION")
verweist auf eine nicht vorhandene Tabelle.
Das Mittel ist das gleiche, referenzieren Sie die Tabelle nach Index:
If ds.Tables(0).Rows.Count > 0 Then
Siehe auch DataTable-Klasse .
If myFoo.Bar.Items IsNot Nothing Then
...
Der Code wird nur getestet, Items
während beide myFoo
und Bar
möglicherweise auch nichts sind. Das Mittel besteht darin, die gesamte Kette oder den gesamten Pfad von Objekten einzeln zu testen:
If (myFoo IsNot Nothing) AndAlso
(myFoo.Bar IsNot Nothing) AndAlso
(myFoo.Bar.Items IsNot Nothing) Then
....
AndAlso
ist wichtig. Nachfolgende Tests werden nicht durchgeführt, sobald die erste False
Bedingung erfüllt ist. Auf diese Weise kann der Code sicher eine Ebene nach der anderen in das Objekt (die Objekte) "bohren" myFoo.Bar
und erst dann auswerten, wenn (und wenn) myFoo
festgestellt wurde, dass es gültig ist. Objektketten oder -pfade können beim Codieren komplexer Objekte sehr lang werden:
myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")
Es ist nicht möglich, irgendetwas 'stromabwärts' eines null
Objekts zu referenzieren . Dies gilt auch für Kontrollen:
myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
Hier myWebBrowser
oder Document
könnte nichts sein oder das formfld1
Element kann nicht existieren.
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)
Dieser Code geht unter anderem nicht davon aus, dass der Benutzer möglicherweise etwas in einem oder mehreren UI-Steuerelementen nicht ausgewählt hat. ListBox1.SelectedItem
kann gut sein Nothing
, so ListBox1.SelectedItem.ToString
wird in einem NRE führen.
Abhilfe
Überprüfen Sie die Daten, bevor Sie sie verwenden (verwenden Sie auch Option Strict
und SQL-Parameter):
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
Alternativ können Sie verwenden (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
Dies ist ein ziemlich üblicher Weg, um ein NRE zu erhalten. In C # meldet die IDE abhängig davon, wie sie codiert ist, dass Controls
sie im aktuellen Kontext nicht vorhanden ist oder "nicht auf ein nicht statisches Mitglied verweisen kann". Bis zu einem gewissen Grad handelt es sich also nur um eine VB-Situation. Es ist auch komplex, weil es zu einer Ausfallkaskade führen kann.
Die Arrays und Sammlungen können auf diese Weise nicht initialisiert werden. Dieser Initialisierungscode wird ausgeführt, bevor der Konstruktor das Form
oder das erstellt Controls
. Als Ergebnis:
somevar
Zuweisung führt zu einer sofortigen NRE, da Nothing keine .Text
Eigenschaft hatWenn Sie später auf Array-Elemente verweisen, erhalten Sie eine NRE. Wenn Sie dies Form_Load
aufgrund eines seltsamen Fehlers tun , meldet die IDE die Ausnahme möglicherweise nicht , wenn sie auftritt. Die Ausnahme wird später angezeigt, wenn Ihr Code versucht, das Array zu verwenden. Diese "stille Ausnahme" wird in diesem Beitrag detailliert beschrieben . Für unsere Zwecke ist der Schlüssel, dass, wenn beim Erstellen eines Formulars ( Sub New
oder Form Load
Ereignisses) etwas Katastrophales passiert , Ausnahmen möglicherweise nicht gemeldet werden, der Code die Prozedur beendet und nur das Formular anzeigt.
Da kein anderer Code in Ihrem Sub New
oder Form Load
Ereignis nach dem NRE ausgeführt wird, können viele andere Dinge nicht initialisiert werden.
Sub Form_Load(..._
'...
Dim name As String = NameBoxes(2).Text ' NRE
' ...
' More code (which will likely not be executed)
' ...
End Sub
Beachten Sie, dass dies für alle Steuerungs- und Komponentenreferenzen gilt, die diese illegal machen, wo sie sind:
Public Class Form1
Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
Private studentName As String = TextBox13.Text
Teilweise Abhilfe
Es ist merkwürdig , dass VB keine Warnung liefern, aber die Lösung ist zu erklären , die Behälter auf Formularebene, sondern initialisieren sie in Form Load - Ereignishandler , wenn die Kontrollen nicht existieren. Dies kann erfolgen, Sub New
solange sich Ihr Code nach dem InitializeComponent
Aufruf befindet:
' 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
Der Array-Code ist möglicherweise noch nicht aus dem Wald. Alle Steuerelemente, die sich in einem Containersteuerelement befinden (wie ein GroupBox
oder Panel
), werden nicht in gefunden Me.Controls
. Sie befinden sich in der Controls-Auflistung dieses Panels oder dieser GroupBox. Ein Steuerelement wird auch nicht zurückgegeben, wenn der Steuerelementname falsch geschrieben ist ( "TeStBox2"
). In solchen Fällen Nothing
wird erneut in diesen Array-Elementen gespeichert und es entsteht eine NRE, wenn Sie versuchen, darauf zu verweisen.
Diese sollten jetzt leicht zu finden sein, da Sie wissen, wonach Sie suchen:
"Button2" befindet sich auf a Panel
Abhilfe
Verwenden Sie anstelle indirekter namentlicher Verweise in der Formularsammlung Controls
die Steuerelementreferenz:
' 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
Dies ist ein Fall, in dem die IDE Sie warnt, dass nicht alle Pfade einen Wert zurückgeben und NullReferenceException
möglicherweise ein Ergebnis resultiert . Sie können die Warnung unterdrücken durch Ersetzen Exit Function
mit Return Nothing
, aber das löst nicht das Problem. Alles, was versucht, die Rückgabe zu verwenden, someCondition = False
führt zu einem NRE:
bList = myFoo.BarList()
For Each b As Bar in bList ' EXCEPTION
...
Abhilfe
Ersetzen Sie Exit Function
in der Funktion durch Return bList
. Das Zurückgeben eines Leerzeichens List
ist nicht dasselbe wie das Zurückgeben Nothing
. Wenn die Möglichkeit besteht, dass ein zurückgegebenes Objekt vorhanden ist Nothing
, testen Sie es , bevor Sie es verwenden:
bList = myFoo.BarList()
If bList IsNot Nothing Then...
Ein schlecht implementierter Try / Catch kann verbergen, wo das Problem liegt, und zu neuen führen:
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
Dies ist ein Fall, in dem ein Objekt nicht wie erwartet erstellt wird, sondern auch die Gegennutzen eines leeren Objekts demonstriert Catch
.
Es gibt ein zusätzliches Komma in der SQL (nach 'Mailadresse'), was zu einer Ausnahme bei führt .ExecuteReader
. Nachdem der Catch
nichts tut, Finally
versucht er eine Bereinigung durchzuführen, aber da Sie kein Close
Nullobjekt können DataReader
, NullReferenceException
ergibt sich ein brandneues Ergebnis.
Ein leerer Catch
Block ist der Spielplatz des Teufels. Dieser OP war verblüfft, warum er eine NRE im Finally
Block bekam. In anderen Situationen kann ein Leerlauf Catch
dazu führen, dass etwas anderes viel weiter stromabwärts durcheinander gerät und Sie Zeit damit verbringen, die falschen Dinge am falschen Ort für das Problem zu suchen. (Die oben beschriebene "stille Ausnahme" bietet den gleichen Unterhaltungswert.)
Abhilfe
Verwenden Sie keine leeren Try / Catch-Blöcke - lassen Sie den Code abstürzen, damit Sie a) die Ursache identifizieren, b) den Ort identifizieren und c) ein geeignetes Mittel anwenden können. Try / Catch-Blöcke sollen keine Ausnahmen vor der Person verbergen, die für die Behebung dieser Probleme eindeutig qualifiziert ist - dem Entwickler.
For Each row As DataGridViewRow In dgvPlanning.Rows
If Not IsDBNull(row.Cells(0).Value) Then
...
Die IsDBNull
Funktion wird verwendet, um zu testen, ob ein Wert gleich ist System.DBNull
: Von MSDN:
Der Wert System.DBNull gibt an, dass das Objekt fehlende oder nicht vorhandene Daten darstellt. DBNull ist nicht dasselbe wie Nothing, was darauf hinweist, dass eine Variable noch nicht initialisiert wurde.
Abhilfe
If row.Cells(0) IsNot Nothing Then ...
Nach wie vor können Sie auf Nothing und dann auf einen bestimmten Wert testen:
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
Beispiel 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
Gibt das erste Element oder den Standardwert zurück, der Nothing
für Referenztypen gilt und niemals DBNull
:
If getFoo IsNot Nothing Then...
Dim chk As CheckBox
chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
Return chk
End If
Wenn ein CheckBox
with chkName
nicht gefunden werden kann (oder in a vorhanden ist GroupBox
), chk
ist Nothing und der Versuch, auf eine Eigenschaft zu verweisen, führt zu einer Ausnahme.
Abhilfe
If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
Die DGV hat einige Macken, die regelmäßig gesehen werden:
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"
Wenn dies der Fall dgvBooks
ist AutoGenerateColumns = True
, werden die Spalten erstellt, aber nicht benannt, sodass der obige Code fehlschlägt, wenn er nach Namen referenziert wird.
Abhilfe
Benennen Sie die Spalten manuell oder referenzieren Sie sie nach Index:
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
Wenn Sie DataGridView
hat AllowUserToAddRows
als True
(Standardeinstellung), die Cells
in dem Rohling / neue Zeile am unteren Rande wird alle enthalten Nothing
. Die meisten Versuche, den Inhalt zu verwenden (zum Beispiel ToString
), führen zu einer NRE.
Abhilfe
Verwenden Sie eine For/Each
Schleife und testen Sie die IsNewRow
Eigenschaft, um festzustellen, ob es sich um die letzte Zeile handelt. Dies funktioniert unabhängig davon, ob AllowUserToAddRows
es wahr ist oder nicht:
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
Wenn Sie eine For n
Schleife verwenden, ändern Sie die Zeilenanzahl oder verwenden Sie, Exit For
wenn IsNewRow
true ist.
Unter bestimmten Umständen kann der Versuch, ein Element zu verwenden, von My.Settings
dem StringCollection
a stammt, bei der ersten Verwendung zu einer NullReferenz führen. Die Lösung ist dieselbe, aber nicht so offensichtlich. Erwägen:
My.Settings.FooBars.Add("ziggy") ' foobars is a string collection
Da VB die Einstellungen für Sie verwaltet, ist zu erwarten, dass die Sammlung initialisiert wird. Dies ist jedoch nur möglich, wenn Sie zuvor einen ersten Eintrag zur Sammlung hinzugefügt haben (im Einstellungseditor). Da die Sammlung (anscheinend) beim Hinzufügen eines Elements initialisiert wird, bleibt sie erhaltenNothing
wenn im Einstellungseditor keine Elemente zum Hinzufügen vorhanden sind.
Abhilfe
Initialisieren Sie die Einstellungssammlung im Load
Ereignishandler des Formulars , falls erforderlich.
If My.Settings.FooBars Is Nothing Then
My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If
In der Regel muss die Settings
Sammlung nur beim ersten Ausführen der Anwendung initialisiert werden. Eine alternative Abhilfe besteht darin, Ihrer Sammlung unter Projekt -> Einstellungen | einen Anfangswert hinzuzufügen FooBars , speichern Sie das Projekt und entfernen Sie den gefälschten Wert.
Sie haben wahrscheinlich den New
Operator vergessen .
oder
Etwas, von dem Sie angenommen haben, dass es fehlerfrei funktioniert, um ein initialisiertes Objekt an Ihren Code zurückzugeben, hat dies nicht getan.
Ignorieren Sie Compiler-Warnungen nicht (jemals) und verwenden Sie sie Option Strict On
(immer).
Ein anderes Szenario ist, wenn Sie ein Nullobjekt in einen Werttyp umwandeln . Zum Beispiel der folgende Code:
object o = null;
DateTime d = (DateTime)o;
Es wird einen NullReferenceException
auf die Besetzung werfen. Im obigen Beispiel scheint dies ziemlich offensichtlich zu sein, aber dies kann in komplizierteren Szenarien geschehen, in denen das Nullobjekt von einem Code zurückgegeben wurde, den Sie nicht besitzen, und die Umwandlung beispielsweise von einem automatischen System generiert wird.
Ein Beispiel hierfür ist dieses einfache ASP.NET-Bindungsfragment mit dem Kalendersteuerelement:
<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />
Hier SelectedDate
handelt es sich tatsächlich um eine Eigenschaft - vom DateTime
Typ - vom Typ Calendar
Web Control, und die Bindung könnte perfekt etwas Null zurückgeben. Der implizite ASP.NET-Generator erstellt einen Code, der dem obigen Cast-Code entspricht. Und dies führt zu einem Problem NullReferenceException
, das ziemlich schwer zu erkennen ist, da es in von ASP.NET generiertem Code liegt, der sich gut kompilieren lässt ...
DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Dies bedeutet, dass die betreffende Variable auf nichts zeigt. Ich könnte das so erzeugen:
SqlConnection connection = null;
connection.Open();
Das wird den Fehler auslösen, denn während ich die Variable " connection
" deklariert habe , zeigt sie auf nichts. Wenn ich versuche, das Mitglied " Open
" aufzurufen , gibt es keinen Verweis, den es lösen könnte, und es wird den Fehler auslösen.
So vermeiden Sie diesen Fehler:
object == null
.Das Resharper-Tool von JetBrains identifiziert jede Stelle in Ihrem Code, an der möglicherweise ein Nullreferenzfehler auftritt, sodass Sie eine Nullprüfung durchführen können. Dieser Fehler ist meiner Meinung nach die häufigste Fehlerquelle.
Dies bedeutet, dass Ihr Code eine Objektreferenzvariable verwendet hat, die auf null gesetzt wurde (dh keine tatsächliche Objektinstanz referenziert hat).
Um den Fehler zu vermeiden, sollten Objekte, die null sein könnten, vor der Verwendung auf null getestet werden.
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
}
Beachten Sie, dass die Ursache in .NET unabhängig vom Szenario immer dieselbe ist:
Sie versuchen, eine Referenzvariable zu verwenden, deren Wert
Nothing
/ istnull
. Wenn der WertNothing
/ istnull
für die Referenzvariable ist, bedeutet dies, dass er keinen Verweis auf eine Instanz eines Objekts enthält, das auf dem Heap vorhanden ist.Sie haben der Variablen entweder nie etwas zugewiesen, nie eine Instanz des der Variablen zugewiesenen Werts erstellt oder die Variable auf
Nothing
/null
manuell gesetzt oder eine Funktion aufgerufen, die die Variable aufNothing
/null
für Sie gesetzt hat.
Ein Beispiel für diese Ausnahme ist: Wenn Sie versuchen, etwas zu überprüfen, ist dies null.
Zum Beispiel:
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
}
Die .NET-Laufzeit löst eine NullReferenceException aus, wenn Sie versuchen, eine Aktion für etwas auszuführen, das nicht instanziiert wurde, z. B. den obigen Code.
Im Vergleich zu einer ArgumentNullException, die normalerweise als Abwehrmaßnahme ausgelöst wird, wenn eine Methode erwartet, dass das, was an sie übergeben wird, nicht null ist.
Weitere Informationen finden Sie in C # NullReferenceException und Null Parameter .
Update C # 8.0, 2019: Nullable Referenztypen
C # 8.0 führt nullbare Referenztypen und nicht nullbare Referenztypen ein . Daher müssen nur nullfähige Referenztypen überprüft werden, um eine NullReferenceException zu vermeiden .
Wenn Sie keinen Referenztyp initialisiert haben und eine seiner Eigenschaften festlegen oder lesen möchten, wird eine NullReferenceException ausgelöst .
Beispiel:
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
Sie können dies einfach vermeiden, indem Sie überprüfen, ob die Variable nicht null ist:
Person p = null;
if (p!=null)
{
p.Name = "Harry"; // Not going to run to this point
}
Um vollständig zu verstehen , warum ein Nullreferenceexception geworfen wird, ist es wichtig , den Unterschied zwischen wissen , Werttypen und [Referenztypen] [3].
Also, wenn Sie es zu tun Werttypen können NullReferenceExceptions nicht auftreten. Allerdings müssen Sie beim Umgang mit Referenztypen wachsam sein !
Nur Referenztypen können, wie der Name schon sagt, Referenzen enthalten oder buchstäblich auf nichts (oder 'null') verweisen. Während Werttypen immer einen Wert enthalten.
Referenztypen (diese müssen überprüft werden):
Werttypen (Sie können diese einfach ignorieren):
Ein weiterer Fall, in dem NullReferenceExceptions
dies passieren kann, ist die (falsche) Verwendung des as
Operators :
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
Hier Book
und Car
sind inkompatible Typen; a Car
kann nicht konvertiert / in a umgewandelt werden Book
. Wenn diese Besetzung fehlschlägt, as
kehrt sie zurück null
. Die Verwendung mybook
danach bewirkt aNullReferenceException
.
Im Allgemeinen sollten Sie eine Besetzung verwenden oder as
wie folgt:
Wenn Sie erwarten, dass die Typkonvertierung immer erfolgreich ist (dh Sie wissen, was das Objekt im Voraus sein sollte), sollten Sie eine Umwandlung verwenden:
ComicBook cb = (ComicBook)specificBook;
Wenn Sie sich über den Typ nicht sicher sind, aber versuchen möchten, ihn als bestimmten Typ zu verwenden, verwenden Sie as
:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
Sie verwenden das Objekt, das die Nullwertreferenz enthält. Es gibt also eine Null-Ausnahme. Im Beispiel ist der Zeichenfolgenwert null, und bei der Überprüfung seiner Länge ist die Ausnahme aufgetreten.
Beispiel:
string value = null;
if (value.Length == 0) // <-- Causes exception
{
Console.WriteLine(value); // <-- Never reached
}
Der Ausnahmefehler ist:
Unbehandelte Ausnahme:
System.NullReferenceException: Objektreferenz nicht auf eine Instanz eines Objekts festgelegt. bei Program.Main ()
Während die Ursachen für NullReferenceExceptions und Ansätze zur Vermeidung / Behebung einer solchen Ausnahme in anderen Antworten behandelt wurden, haben viele Programmierer noch nicht gelernt, wie solche Ausnahmen während der Entwicklung unabhängig voneinander debuggt werden können.
In Visual Studio ist dies dank des Visual Studio-Debuggers normalerweise einfach .
Stellen Sie zunächst sicher, dass der richtige Fehler abgefangen wird - siehe Wie erlaube ich das Unterbrechen von 'System.NullReferenceException' in VS2010? Anmerkung 1
Beginnen Sie dann entweder mit dem Debuggen (F5) oder hängen Sie [den VS-Debugger] an den laufenden Prozess an . Gelegentlich kann die Verwendung hilfreich sein Debugger.Break
, um den Debugger zu starten.
Wenn nun die NullReferenceException ausgelöst (oder nicht behandelt) wird, stoppt der Debugger in der Zeile, in der die Ausnahme aufgetreten ist (erinnern Sie sich an den oben festgelegten Regelsatz?). Manchmal ist der Fehler leicht zu erkennen.
In der folgenden Zeile ist der einzige Code, der die Ausnahme verursachen kann, der myString
Wert null. Dies kann überprüft werden, indem Sie das Überwachungsfenster betrachten oder Ausdrücke im Direktfenster ausführen .
var x = myString.Trim();
In fortgeschritteneren Fällen, wie den folgenden, müssen Sie eine der oben genannten Techniken (Watch oder Immediate Windows) verwenden, um die Ausdrücke zu überprüfen, um festzustellen, ob str1
null oder str2
null waren.
var x = str1.Trim() + str2.Trim();
Einmal wo die Ausnahme throw ist lokalisiert worden, es Grund rückwärts in der Regel trivial ist, um herauszufinden , wo der Nullwert [falsch] eingeführt wurde -
Nehmen Sie sich Zeit, um die Ursache der Ausnahme zu verstehen. Auf Nullausdrücke prüfen. Überprüfen Sie die vorherigen Ausdrücke, die zu solchen Nullausdrücken hätten führen können. Fügen Sie Haltepunkte hinzu und führen Sie das Programm entsprechend durch.Verwenden Sie den Debugger.
1 Wenn Break on Throws zu aggressiv ist und der Debugger auf einer NPE in der .NET- oder Drittanbieter-Bibliothek stoppt, kann Break on User-Unhandled verwendet werden, um die abgefangenen Ausnahmen zu begrenzen. Darüber hinaus führt VS2012 Just My Code ein, das ich ebenfalls aktivieren möchte .
Wenn Sie mit aktiviertem Just My Code debuggen, unterscheidet sich das Verhalten geringfügig. Wenn Just My Code aktiviert ist, ignoriert der Debugger CLR-Ausnahmen (Common Language Runtime) der ersten Chance, die außerhalb von My Code ausgelöst werden und My Code nicht durchlaufen
Simon Mourier gab dieses Beispiel :
object o = null;
DateTime d = (DateTime)o; // NullReferenceException
wobei eine Unboxing- Konvertierung (Umwandlung) von object
(oder von einer der Klassen System.ValueType
oder oder System.Enum
oder von einem Schnittstellentyp) in einen Werttyp (außer Nullable<>
) an sich das ergibtNullReferenceException
.
In der anderen Richtung wird eine boxing Umwandlung von a Nullable<>
welches HasValue
gleich false
zu einem Referenz - Typ kann eine geben null
Referenz , die auf ein später führen kann NullReferenceException
. Das klassische Beispiel ist:
DateTime? d = null;
var s = d.ToString(); // OK, no exception (no boxing), returns ""
var t = d.GetType(); // Bang! d is boxed, NullReferenceException
Manchmal geschieht das Boxen auf andere Weise. Zum Beispiel mit dieser nicht generischen Erweiterungsmethode:
public static void MyExtension(this object x)
{
x.ToString();
}
Der folgende Code ist problematisch:
DateTime? d = null;
d.MyExtension(); // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.
Diese Fälle entstehen aufgrund der speziellen Regeln, die die Laufzeit beim Boxen von Nullable<>
Instanzen verwendet.
Hinzufügen eines Falls, in dem der Klassenname für die im Entitätsframework verwendete Entität mit dem Klassennamen für eine Webformular-CodeBehind-Datei identisch ist.
Angenommen, Sie haben ein Webformular Contact.aspx, dessen Codebehind-Klasse Contact ist, und Sie haben einen Entitätsnamen Contact.
Der folgende Code löst dann eine NullReferenceException aus, wenn Sie context.SaveChanges () aufrufen.
Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line
Der Vollständigkeit halber DataContext-Klasse
public class DataContext : DbContext
{
public DbSet<Contact> Contacts {get; set;}
}
und Kontaktentitätsklasse. Manchmal sind Entitätsklassen Teilklassen, sodass Sie sie auch in anderen Dateien erweitern können.
public partial class Contact
{
public string Name {get; set;}
}
Der Fehler tritt auf, wenn sich sowohl die Entität als auch die Codebehind-Klasse im selben Namespace befinden. Um dies zu beheben, benennen Sie die Entitätsklasse oder die Codebehind-Klasse für Contact.aspx um.
Grund Ich bin mir immer noch nicht sicher über den Grund. Aber wenn eine der Entitätsklassen System.Web.UI.Page erweitert, tritt dieser Fehler auf.
Zur Diskussion werfen Sie einen Blick auf NullReferenceException in DbContext.saveChanges ()
Ein weiterer allgemeiner Fall, in dem diese Ausnahme auftreten kann, besteht darin, Klassen während des Komponententests zu verspotten. Unabhängig vom verwendeten Verspottungsframework müssen Sie sicherstellen, dass alle geeigneten Ebenen der Klassenhierarchie ordnungsgemäß verspottet sind. Insbesondere alle Eigenschaften vonHttpContext
verspottet werden, auf die der zu testende Code verweist.
Ein etwas ausführliches Beispiel finden Sie unter " NullReferenceException beim Testen eines benutzerdefinierten AuthorizationAttribute ".
Ich habe eine andere Perspektive als darauf zu antworten. Diese Art von Antworten "Was kann ich noch tun, um dies zu vermeiden? "
Wenn ein Controller über verschiedene Ebenen hinweg arbeitet , beispielsweise in einer MVC-Anwendung, benötigt er Dienste, um Geschäftsvorgänge aufzurufen. In solchen Szenarien kann der Abhängigkeitsinjektionscontainer zum Initialisieren der Dienste verwendet werden, um die NullReferenceException zu vermeiden . Das bedeutet, dass Sie sich keine Gedanken über die Überprüfung auf Null machen müssen und die Dienste einfach vom Controller aus aufrufen müssen, als ob sie immer als Singleton oder Prototyp verfügbar (und initialisiert) wären.
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();
}
}
In Bezug auf "Was soll ich dagegen tun?" Kann es viele Antworten geben.
Eine "formellere" Möglichkeit, solche Fehlerbedingungen während der Entwicklung zu verhindern, besteht darin, das Design in Ihrem Code vertraglich anzuwenden . Diese Mittel müssen Sie Setklasse Invarianten und / oder auch Funktion / Methode Vorbedingungen und Nachbedingungen auf Ihrem System, während der Entwicklung.
Kurz gesagt, Klasseninvarianten stellen sicher, dass es in Ihrer Klasse einige Einschränkungen gibt, die bei normaler Verwendung nicht verletzt werden (und daher wird die Klasse nicht in einen inkonsistenten Zustand versetzt). Voraussetzungen bedeuten, dass Daten, die als Eingabe für eine Funktion / Methode angegeben werden, bestimmten festgelegten Einschränkungen folgen und diese niemals verletzen müssen. Nachbedingungen bedeuten, dass eine Ausgabe von Funktionen / Methoden den festgelegten Einschränkungen erneut folgen muss, ohne sie jemals zu verletzen. Vertragsbedingungen sollten während der Ausführung eines fehlerfreien Programms niemals verletzt werden. Daher wird das vertragliche Design in der Praxis im Debug-Modus überprüft und in Releases deaktiviert , um die Leistung des entwickelten Systems zu maximieren.
Auf diese Weise können Sie NullReferenceException
Fälle vermeiden , die auf eine Verletzung der festgelegten Einschränkungen zurückzuführen sind. Wenn Sie beispielsweise eine Objekteigenschaft X
in einer Klasse verwenden und später versuchen, eine ihrer Methoden aufzurufen, X
die einen Nullwert hat, führt dies zu NullReferenceException
:
public X { get; set; }
public void InvokeX()
{
X.DoSomething(); // if X value is null, you will get a NullReferenceException
}
Wenn Sie jedoch als Methodenvoraussetzung "Eigenschaft X darf niemals einen Nullwert haben" festlegen, können Sie das zuvor beschriebene Szenario verhindern:
//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant ()
{
Contract.Invariant ( X != null );
//...
}
Aus diesem Grund ist das Code Contracts- Projekt für .NET-Anwendungen vorhanden.
Alternativ kann das vertragliche Design unter Verwendung von Behauptungen angewendet werden .
UPDATE: Erwähnenswert ist, dass der Begriff von Bertrand Meyer im Zusammenhang mit seinem Entwurf der Programmiersprache Eiffel geprägt wurde .
A NullReferenceException
wird ausgelöst, wenn wir versuchen, auf Eigenschaften eines Nullobjekts zuzugreifen, oder wenn ein Zeichenfolgenwert leer wird und wir versuchen, auf Zeichenfolgenmethoden zuzugreifen.
Zum Beispiel:
Wenn auf eine Zeichenfolgenmethode einer leeren Zeichenfolge zugegriffen wird:
string str = string.Empty;
str.ToLower(); // throw null reference exception
Wenn auf eine Eigenschaft eines Nullobjekts zugegriffen wird:
Public Class Person {
public string Name { get; set; }
}
Person objPerson;
objPerson.Name /// throw Null refernce Exception
String.Empty.ToLower()
löst keine Nullreferenzausnahme aus. Es stellt eine tatsächliche Zeichenfolge dar, wenn auch eine leere (dh ""
). Da dies ein Objekt zum Aufrufen hat ToLower()
, wäre es nicht sinnvoll, dort eine Nullreferenzausnahme auszulösen.
TL; DR: Versuchen Sie es mit Html.Partial
anstelle vonRenderpage
Ich habe Object reference not set to an instance of an object
festgestellt , dass ich versucht habe, eine Ansicht in einer Ansicht zu rendern , indem ich ihr ein Modell wie folgt gesendet habe:
@{
MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
Das Debuggen zeigte, dass das Modell in MyOtherView Null war. Bis ich es geändert habe zu:
@{
MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);
Und es hat funktioniert.
Außerdem musste ich nicht damit Html.Partial
beginnen, dass Visual Studio manchmal fehlerhaft aussehende, schnörkellose Linien darunter wirft, Html.Partial
wenn es sich in einer anders konstruierten foreach
Schleife befindet, obwohl es nicht wirklich ein Fehler ist:
@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>
Aber ich konnte die Anwendung ohne Probleme mit diesem "Fehler" ausführen. Ich konnte den Fehler beseitigen, indem ich die Struktur der foreach
Schleife so änderte :
@foreach(var M in MyEntities){
...
}
Obwohl ich das Gefühl habe, dass Visual Studio das kaufmännische Und und die Klammern falsch gelesen hat.
Html.Partial
nicht@Html.Partial
Null
), sodass ich wusste, dass der Fehler darin bestand, wie ich das Modell gesendet habe .
Was können Sie dagegen tun?
Hier gibt es viele gute Antworten, die erklären, was eine Nullreferenz ist und wie man sie debuggt. Es gibt jedoch nur sehr wenige Möglichkeiten, um das Problem zu verhindern oder zumindest das Auffinden zu erleichtern.
Überprüfen Sie die Argumente
Beispielsweise können Methoden die verschiedenen Argumente überprüfen, um festzustellen, ob sie null sind, und eine auslösen ArgumentNullException
, eine Ausnahme, die offensichtlich genau für diesen Zweck erstellt wurde.
Der Konstruktor für das ArgumentNullException
Even verwendet den Namen des Parameters und eine Nachricht als Argumente, damit Sie dem Entwickler genau mitteilen können, wo das Problem liegt.
public void DoSomething(MyObject obj) {
if(obj == null)
{
throw new ArgumentNullException("obj", "Need a reference to obj.");
}
}
Verwenden Sie Tools
Es gibt auch mehrere Bibliotheken, die helfen können. "Resharper" kann Sie beispielsweise beim Schreiben von Code warnen , insbesondere wenn Sie das folgende Attribut verwenden: NotNullAttribute
Es gibt "Microsoft Code Contracts", in denen Sie Syntax wie verwenden Contract.Requires(obj != null)
der Sie Laufzeit- und Kompilierungsprüfungen durchführen können: Einführung in Code Contracts .
Es gibt auch "PostSharp", mit dem Sie einfach folgende Attribute verwenden können:
public void DoSometing([NotNull] obj)
Auf diese Weise wird PostSharp zur obj
Laufzeit auf Null gesetzt , wenn es Teil Ihres Erstellungsprozesses ist . Sehen: PostSharp-Nullprüfung
Plain Code-Lösung
Oder Sie können Ihren eigenen Ansatz jederzeit mit einfachem altem Code codieren. Zum Beispiel ist hier eine Struktur, mit der Sie Nullreferenzen abfangen können. Es ist nach dem gleichen Konzept modelliert wie 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 };
}
}
Sie würden sehr ähnlich wie Sie verwenden Nullable<T>
, außer mit dem Ziel, genau das Gegenteil zu erreichen - nicht zuzulassen null
. Hier sind einige Beispiele:
NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null
NotNull<T>
wird implizit von und nach gegossen, T
sodass Sie es fast überall dort verwenden können, wo Sie es benötigen. Sie können beispielsweise ein Person
Objekt an eine Methode übergeben, die Folgendes verwendet NotNull<Person>
:
Person person = new Person { Name = "John" };
WriteName(person);
public static void WriteName(NotNull<Person> person)
{
Console.WriteLine(person.Value.Name);
}
Wie Sie oben wie bei nullable sehen können, würden Sie über die Value
Eigenschaft auf den zugrunde liegenden Wert zugreifen . Alternativ können Sie eine explizite oder implizite Umwandlung verwenden. Sie können ein Beispiel mit dem folgenden Rückgabewert sehen:
Person person = GetPerson();
public static NotNull<Person> GetPerson()
{
return new Person { Name = "John" };
}
Oder Sie können es sogar verwenden, wenn die Methode T
(in diesem Fall Person
) nur durch eine Umwandlung zurückgegeben wird. Der folgende Code möchte beispielsweise nur den obigen Code:
Person person = (NotNull<Person>)GetPerson();
public static Person GetPerson()
{
return new Person { Name = "John" };
}
Mit Erweiterung kombinieren
Kombinieren Sie NotNull<T>
mit einer Erweiterungsmethode und Sie können noch mehr Situationen abdecken. Hier ist ein Beispiel, wie die Erweiterungsmethode aussehen kann:
[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;
}
}
Und hier ist ein Beispiel, wie es verwendet werden könnte:
var person = GetPerson().NotNull();
GitHub
Als Referenz habe ich den obigen Code auf GitHub verfügbar gemacht. Sie finden ihn unter:
https://github.com/luisperezphd/NotNull
Verwandte Sprachfunktion
In C # 6.0 wurde der "nullbedingte Operator" eingeführt, der dabei ein wenig hilft. Mit dieser Funktion können Sie auf verschachtelte Objekte verweisen. Wenn eines davon einer ist, wird null
der gesamte Ausdruck zurückgegeben null
.
Dies reduziert die Anzahl der Nullprüfungen, die Sie in einigen Fällen durchführen müssen. Die Syntax besteht darin, vor jedem Punkt ein Fragezeichen zu setzen. Nehmen Sie zum Beispiel den folgenden Code:
var address = country?.State?.County?.City;
Stellen Sie sich vor, es country
handelt sich um ein Objekt vom Typ Country
mit einer Eigenschaft namens State
usw. Wenn country
, State
, County
oder City
ist null
dann address will be
null . Therefore you only have to check whether
Adresse is
null`.
Es ist eine großartige Funktion, aber es gibt Ihnen weniger Informationen. Es macht nicht klar, welche der 4 null ist.
Eingebaut wie Nullable?
C # hat eine nette Abkürzung für Nullable<T>
, Sie können etwas auf Null setzen, indem Sie ein Fragezeichen nach dem Typ wie folgt setzen int?
.
Es wäre schön, wenn C # so etwas wie die NotNull<T>
obige Struktur und eine ähnliche Abkürzung hätte, vielleicht das Ausrufezeichen (!), Damit Sie etwas schreiben könnten wie : public void WriteName(Person! person)
.
Interessanterweise erwähnt keine der Antworten auf dieser Seite die beiden Randfälle. Ich hoffe, es macht niemandem etwas aus, wenn ich sie hinzufüge:
Generische Wörterbücher in .NET sind nicht threadsicher und werfen manchmal ein NullReference
oder sogar (häufiger) einKeyNotFoundException
auslösen, wenn Sie versuchen, über zwei gleichzeitige Threads auf einen Schlüssel zuzugreifen. Die Ausnahme ist in diesem Fall ziemlich irreführend.
Wenn a NullReferenceException
vom unsafe
Code ausgelöst wird , können Sie sich Ihre Zeigervariablen ansehen und sie überprüfenIntPtr.Zero
oder etwas . Das ist dasselbe ("Nullzeiger-Ausnahme"), aber in unsicherem Code werden Variablen häufig in Werttypen / Arrays usw. umgewandelt, und Sie schlagen Ihren Kopf gegen die Wand und fragen sich, wie ein Werttyp dies auslösen kann Ausnahme.
(Ein weiterer Grund für die Nichtverwendung von unsicherem Code, es sei denn, Sie benötigen ihn übrigens)
null
welcher?
Sie können NullReferenceException auf saubere Weise mithilfe von Null-bedingten Operatoren in c # 6 beheben und weniger Code schreiben, um Nullprüfungen durchzuführen.
Es wird verwendet, um auf Null zu testen, bevor eine Mitgliedszugriffs- (?.) Oder Indexoperation (? [) Ausgeführt wird.
Beispiel
var name = p?.Spouse?.FirstName;
ist äquivalent zu:
if (p != null)
{
if (p.Spouse != null)
{
name = p.Spouse.FirstName;
}
}
Das Ergebnis ist, dass der Name null ist, wenn p null ist oder wenn p.Spouse null ist.
Andernfalls wird dem Variablennamen der Wert von p.Spouse.FirstName zugewiesen.
Weitere Informationen: Nullbedingte Operatoren
Die Fehlerzeile "Objektreferenz nicht auf eine Instanz eines Objekts festgelegt" gibt an, dass Sie einer Objektreferenz kein Instanzobjekt zugewiesen haben und dennoch auf Eigenschaften / Methoden dieses Objekts zugreifen.
Beispiel: Angenommen, Sie haben eine Klasse namens myClass, die eine Eigenschaft prop1 enthält.
public Class myClass
{
public int prop1 {get;set;}
}
Jetzt greifen Sie in einer anderen Klasse wie unten auf diese Requisite zu:
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref.prop1 = 1; //This line throws error
}
}
Die obige Zeile löst einen Fehler aus, weil die Referenz der Klasse myClass deklariert, aber nicht instanziiert ist oder eine Instanz des Objekts nicht der Referenz dieser Klasse zugewiesen ist.
Um dies zu beheben, müssen Sie instanziieren (Objekt der Referenz dieser Klasse zuweisen).
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref = new myClass();
ref.prop1 = 1;
}
}
NullReferenceException oder Objektreferenz, die nicht auf eine Instanz eines Objekts festgelegt ist, tritt auf, wenn ein Objekt der Klasse, die Sie verwenden möchten, nicht instanziiert wird. Zum Beispiel:
Angenommen, Sie haben eine Klasse namens Student.
public class Student
{
private string FirstName;
private string LastName;
public string GetFullName()
{
return FirstName + LastName;
}
}
Stellen Sie sich nun eine andere Klasse vor, in der Sie versuchen, den vollständigen Namen des Schülers abzurufen.
public class StudentInfo
{
public string GetStudentName()
{
Student s;
string fullname = s.GetFullName();
return fullname;
}
}
Wie im obigen Code zu sehen ist, deklariert die Anweisung Student s - nur die Variable vom Typ Student. Beachten Sie, dass die Student-Klasse zu diesem Zeitpunkt nicht instanziiert wird. Wenn die Anweisung s.GetFullName () ausgeführt wird, wird daher die NullReferenceException ausgelöst.
Nun, in einfachen Worten:
Sie versuchen, auf ein Objekt zuzugreifen, das nicht erstellt wurde oder sich derzeit nicht im Speicher befindet.
Wie kann man das angehen?
Debuggen und den Debugger brechen lassen ... Sie gelangen direkt zu der defekten Variablen ... Jetzt müssen Sie dies einfach beheben. Verwenden Sie das neue Schlüsselwort an der entsprechenden Stelle.
Wenn dies bei einigen Datenbankbefehlen verursacht wird , weil das Objekt nicht vorhanden ist, müssen Sie lediglich eine Nullprüfung durchführen und damit umgehen:
if (i == null) {
// Handle this
}
Am schwierigsten ist es, wenn der GC das Objekt bereits erfasst hat. Dies tritt im Allgemeinen auf, wenn Sie versuchen, ein Objekt mithilfe von Zeichenfolgen zu finden. Wenn Sie es also anhand des Objektnamens suchen, kann es vorkommen, dass der GC dies bereits tut bereinigt ... Dies ist schwer zu finden und wird zu einem ziemlichen Problem ... Eine bessere Möglichkeit, dies zu beheben, besteht darin, während des Entwicklungsprozesses erforderlichenfalls Nullprüfungen durchzuführen. So sparen Sie viel Zeit.
Mit Suchen nach Namen meine ich, dass einige Frameworks es Ihnen ermöglichen, FIndObjects mithilfe von Zeichenfolgen zu finden, und der Code könnte folgendermaßen aussehen: FindObject ("ObjectName");
Der einfachste Weg, eine NullReferenceExeption zu reparieren, hat buchstäblich zwei Möglichkeiten. Wenn Sie beispielsweise ein GameObject mit einem angehängten Skript und einer Variablen namens rb (Rigidbody) haben, wird diese Variable beim Starten Ihres Spiels mit null gestartet.
Aus diesem Grund erhalten Sie eine NullReferenceExeption, da auf dem Computer keine Daten in dieser Variablen gespeichert sind.
Ich werde eine RigidBody-Variable als Beispiel verwenden.
Wir können Daten wirklich einfach auf verschiedene Arten hinzufügen:
rb = GetComponent<Rigidbody>();
Start()
oder Awake()
Funktionen. rb = AddComponent<RigidBody>();
Weitere Hinweise: Wenn Sie möchten, dass Unity Ihrem Objekt eine Komponente hinzufügt, und Sie möglicherweise vergessen haben, eine Komponente hinzuzufügen, können Sie [RequireComponent(typeof(RigidBody))]
über Ihrer Klassendeklaration (dem Leerzeichen unter all Ihren Verwendungen) etwas eingeben.
Viel Spaß beim Spielen!
Wenn wir häufige Szenarien betrachten, in denen diese Ausnahme ausgelöst werden kann, greifen Sie auf Eigenschaften mit dem Objekt oben zu.
Ex:
string postalcode=Customer.Address.PostalCode;
//if customer or address is null , this will through exeption
Wenn hier die Adresse null ist, erhalten Sie die NullReferenceException.
In der Praxis sollten wir daher immer die Nullprüfung verwenden, bevor wir auf Eigenschaften in solchen Objekten zugreifen (insbesondere bei generischen Objekten).
string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
Dies ist im Grunde eine Nullreferenzausnahme . Wie Microsoft feststellt-
Eine NullReferenceException-Ausnahme wird ausgelöst, wenn Sie versuchen, auf ein Mitglied eines Typs zuzugreifen, dessen Wert null ist.
Das heißt, wenn ein Mitglied, das keinen Wert hat und wir dieses Mitglied dazu bringen, eine bestimmte Aufgabe auszuführen, wird das System zweifellos eine Nachricht werfen und sagen:
"Hey, warte, dieses Mitglied hat keine Werte, also kann es die Aufgabe, die du ihm übergibst, nicht ausführen."
Die Ausnahme selbst besagt, dass etwas verwiesen wird, dessen Wert jedoch nicht festgelegt wird. Dies bedeutet also, dass es nur bei Verwendung von Referenztypen auftritt, da Werttypen nicht nullwertfähig sind.
NullReferenceException tritt nicht auf, wenn Mitglieder vom Typ Wert verwendet werden.
class Program
{
static void Main(string[] args)
{
string str = null;
Console.WriteLine(str.Length);
Console.ReadLine();
}
}
Der obige Code zeigt eine einfache Zeichenfolge, die mit einem Nullwert belegt ist.
Wenn ich jetzt versuche, die Länge der Zeichenfolge str zu drucken , wird eine nicht behandelte Ausnahme vom Typ 'System.NullReferenceException' angezeigt, da Mitglied str auf null zeigt und es keine Länge von null geben kann.
' NullReferenceException ' tritt auch auf, wenn wir vergessen, einen Referenztyp zu instanziieren.
Angenommen, ich habe eine Klassen- und Mitgliedsmethode. Ich habe meine Klasse nicht instanziiert, sondern nur meine Klasse benannt. Wenn ich nun versuche, die Methode zu verwenden, gibt der Compiler einen Fehler aus oder gibt eine Warnung aus (abhängig vom Compiler).
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");
}
}
Der Compiler für den obigen Code löst einen Fehler aus, dass die Variable obj nicht zugewiesen ist, was bedeutet, dass unsere Variable Nullwerte oder nichts hat. Der Compiler für den obigen Code löst einen Fehler aus, dass die Variable obj nicht zugewiesen ist, was bedeutet, dass unsere Variable Nullwerte oder nichts hat.
NullReferenceException entsteht aufgrund unseres Fehlers, den Wert des Objekts nicht zu überprüfen. Wir lassen die Objektwerte in der Codeentwicklung oft unkontrolliert.
Es entsteht auch, wenn wir vergessen, unsere Objekte zu instanziieren. Die Verwendung dieser Methode kann auch die Verwendung von Methoden, Eigenschaften, Sammlungen usw. sein, die Nullwerte zurückgeben oder festlegen können.
Es gibt verschiedene Möglichkeiten und Methoden, um diese bekannte Ausnahme zu vermeiden:
Explizite Überprüfung: Wir sollten uns an die Tradition halten, Objekte, Eigenschaften, Methoden, Arrays und Sammlungen zu überprüfen, ob sie null sind. Dies kann einfach mithilfe von bedingten Anweisungen wie if-else if-else usw. implementiert werden.
Ausnahmebehandlung: Eine der wichtigsten Möglichkeiten zur Verwaltung dieser Ausnahme. Mit einfachen Try-Catch-finally-Blöcken können wir diese Ausnahme steuern und auch ein Protokoll darüber führen. Dies kann sehr nützlich sein, wenn sich Ihre Anwendung in der Produktionsphase befindet.
Null-Operatoren: Null-Coalescing-Operatoren und Null-Bedingungsoperatoren können auch nützlich sein, wenn Werte für Objekte, Variablen, Eigenschaften und Felder festgelegt werden.
Debugger: Für Entwickler haben wir die große Waffe des Debuggens dabei. Wenn wir während der Entwicklung mit NullReferenceException konfrontiert sind, können wir den Debugger verwenden, um zur Quelle der Ausnahme zu gelangen.
Eingebaute Methode: Systemmethoden wie GetValueOrDefault (), IsNullOrWhiteSpace () und IsNullorEmpty () suchen nach Nullen und weisen den Standardwert zu, wenn ein Nullwert vorhanden ist.
Hier gibt es bereits viele gute Antworten. Sie können auch eine detailliertere Beschreibung mit Beispielen in meinem Blog überprüfen .
Hoffe das hilft auch!
Wenn diese Meldung beim Speichern oder Kompilieren des Builds angezeigt wird, schließen Sie einfach alle Dateien und öffnen Sie eine beliebige Datei zum Kompilieren und Speichern.
Für mich war der Grund, dass ich die Datei umbenannt hatte und die alte Datei noch offen war.