it-swarm.com.de

Was ist eine NullReferenceException und wie behebe ich sie?

Ich habe Code und wenn er ausgeführt wird, wirft er ein NullReferenceException mit den Worten:

Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt.

Was bedeutet das und was kann ich tun, um diesen Fehler zu beheben?

1876
John Saunders

Was ist die Ursache?

Endeffekt

Sie versuchen, etwas zu verwenden, das null (oder Nothing in VB.NET) ist. Dies bedeutet, dass Sie entweder null festgelegt haben oder dass Sie überhaupt nichts festgelegt haben.

Wie alles andere wird auch null herumgereicht. Wenn es nullinMethode "A" ist, könnte es sein, dass Methode "B" ein nullanübergeben hat. Methode "A".

null kann verschiedene Bedeutungen haben:

  1. Objektvariablen, die nicht initialisiert ​​und daher auf nichts hindeuten. In diesem Fall führt der Zugriff auf Eigenschaften oder Methoden solcher Objekte zu einem NullReferenceException.
  2. Der Entwickler ist mit null absichtlich angeben, dass kein sinnvoller Wert verfügbar ist. Beachten Sie, dass C # das Konzept nullfähiger Datentypen für Variablen hat (Datenbanktabellen können nullfähige Felder haben) - Sie können ihnen null zuweisen Gibt an, dass kein Wert gespeichert ist, z. B. int? a = null;, wobei das Fragezeichen angibt, dass in der Variablen a der Wert null gespeichert werden darf. Sie können dies entweder mit if (a.HasValue) {...} oder mit if (a==null) {...} überprüfen. Nullfähige Variablen, wie in diesem Beispiel a, ermöglichen den expliziten Zugriff auf den Wert über a.Value oder ganz normal über a.
    Hinweis Wenn Sie über a.Value darauf zugreifen, wird anstelle von InvalidOperationException ein NullReferenceException ausgegeben, wenn anull ist int b; dann sollten Sie Aufgaben wie if (a.HasValue) { b = a.Value; } oder kürzere if (a != null) { b = a; } ausführen.

Der Rest dieses Artikels geht ausführlicher auf Fehler ein, die viele Programmierer häufig machen und die zu einem NullReferenceException führen können.

Genauer

Die Laufzeit, die ein NullReferenceException auslöst immer bedeutet dasselbe: Sie versuchen, eine Referenz zu verwenden, und die Referenz ist nicht initialisiert (oder es wareinmalinitialisiert, ist abernicht mehrinitialisiert).

Dies bedeutet, dass die Referenz null lautet und Sie nicht über eine null-Referenz auf Mitglieder (z. B. Methoden) zugreifen können. Der einfachste Fall:

string foo = null;
foo.ToUpper();

Dadurch wird in der zweiten Zeile ein NullReferenceException ausgegeben, da Sie die Instanzmethode ToUpper() nicht für einen string-Verweis aufrufen können, der auf null verweist.

Debuggen

Wie finden Sie die Quelle eines NullReferenceException? Abgesehen von 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 den Mauszeiger darüber halten Öffnen Sie ein (Quick) Watch-Fenster oder verwenden Sie die verschiedenen Debug-Fenster wie Locals und Autos.

Wenn Sie herausfinden möchten, wo sich die Referenz befindet 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 Ort einen Haltepunkt setzen und Ihr Programm mit dem angehängten Debugger ausführen. Bei jeder Unterbrechung des Debuggers an einem solchen Haltepunkt 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 wurde.

Beispiele

Einige häufige Szenarien, in denen die Ausnahme ausgelöst werden kann:

Generisch

ref1.ref2.ref3.member

Wenn ref1 oder ref2 oder ref3 null ist, erhalten Sie ein NullReferenceException. Wenn Sie das Problem lösen möchten, ermitteln Sie, 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 kann HttpContext.Current null sein, oder die Eigenschaft User kann null sein, oder die Eigenschaft Identity kann null sein.

Indirekt

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 Nullreferenz child (Person) vermeiden möchten, können Sie sie im Konstruktor des übergeordneten Objekts (Book) initialisieren.

Verschachtelte Objektinitialisierer

Gleiches gilt für verschachtelte Objektinitialisierer:

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

Dies übersetzt zu

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

Während das Schlüsselwort new verwendet wird, wird nur eine neue Instanz von Book erstellt, jedoch keine neue Instanz von Person. Daher ist die Eigenschaft Author immer noch null.

Initialisierer für verschachtelte Auflistungen

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

Die verschachtelten Auflistungsinitialisierer verhalten sich wie folgt:

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

Dies übersetzt zu

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

Der new Person erstellt nur eine Instanz von Person, die Books-Auflistung ist jedoch immer noch null. Die Syntax des Auflistungsinitialisierers erstellt keine Auflistung für p1.Books, sondern übersetzt nur die Anweisungen p1.Books.Add(...).

Array

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

Array-Elemente

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.

Gezackte Arrays

long[][] array = new long[1][];
array[0][0] = 3; // is null because only the first dimension is yet initialized.
                 // Use array[0] = new long[2]; first.

Sammlung/Liste/Wörterbuch

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

Bereichsvariable (indirekt/verzögert)

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.

Veranstaltungen

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

Schlechte Namenskonventionen:

Wenn Sie Felder anders benannt haben als Einheimische, haben Sie möglicherweise festgestellt, dass Sie das Feld nie initialisiert haben.

public class Form1 {
    private Customer customer;

    private void Form1_Load(object sender, EventArgs e) {
        Customer customer = new Customer();
        customer.Name = "John";
    }

    private void Button_Click(object sender, EventArgs e) {
        MessageBox.Show(customer.Name);
    }
}

Dies kann durch Befolgen der Konvention zum Präfixieren von Feldern mit einem Unterstrich gelöst werden:

private Customer _customer;

ASP.NET Page Lebenszyklus:

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

ASP.NET-Sitzungswerte

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

Leere ASP.NET MVC-Ansichtsmodelle

Wenn die Ausnahme beim Verweisen auf eine Eigenschaft von @Model in einer ASP.NET MVC-Ansicht auftritt, müssen Sie verstehen, dass Model in Ihrer Aktionsmethode festgelegt wird, wenn Sie return eine Ansicht aufrufen. 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 -->

Erstellungsreihenfolge und Ereignisse für WPF-Steuerelemente

WPF-Steuerelemente werden beim Aufruf von InitializeComponent in der Reihenfolge erstellt, in der sie in der visuellen Struktur angezeigt werden. Bei früh erstellten Steuerelementen mit Ereignishandlern usw., die während von NullReferenceException ausgelöst werden und auf neu erstellte Steuerelemente verweisen, wird ein InitializeComponent ausgelöst.

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 wird comboBox1 vor label1 erstellt. Wenn comboBox1_SelectionChanged versucht, auf `label1 zu verweisen, wurde es noch nicht erstellt.

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

Das Ändern der Reihenfolge der Deklarationen in der XAML (d. H. Auflisten von label1 vor comboBox1, Ignorieren von Problemen der Entwurfsphilosophie, würde zumindest die NullReferenceException hier beheben.

Besetzung mit as

var myThing = someObject as Thing;

Dies löst keine InvalidCastException aus, sondern gibt null zurück, wenn die Umwandlung fehlschlägt (und someObject selbst null ist). Also sei dir dessen bewusst.

LINQ FirstOrDefault () und SingleOrDefault ()

Die einfachen Versionen First() und Single() lösen Ausnahmen aus, wenn nichts vorhanden ist. Die "OrDefault" -Versionen geben in diesem Fall null zurück. Also sei dir dessen bewusst.

für jeden

foreach wird ausgelöst, wenn Sie versuchen, die Nullsammlung zu wiederholen. Normalerweise verursacht durch unerwartete null -Ergebnisse von Methoden, die Auflistungen 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"))

Möglichkeiten zu vermeiden

Suchen Sie explizit nach null und ignorieren Sie Nullwerte.

Wenn Sie erwarten, dass die Referenz manchmal null ist, können Sie überprüfen, ob sie null ist, bevor Sie auf Instanzmitglieder zugreifen:

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

Suchen Sie explizit nach null und geben Sie einen Standardwert an.

Von Ihnen erwartete Methoden, die eine Instanz zurückgeben, können null zurückgeben, z. B. wenn das gesuchte Objekt nicht gefunden werden kann. Sie können einen Standardwert zurückgeben, wenn dies der Fall ist:

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

Suchen Sie explizit nach null in 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;
}

Verwenden Sie Debug.Assert, wenn ein Wert niemals null lauten soll, um das Problem früher zu beheben, als die Ausnahme auftritt.

Wenn Sie während der Entwicklung wissen, dass eine Methode möglicherweise null zurückgeben kann, aber niemals sollte, können Sie Debug.Assert() verwenden, um den Vorgang so schnell wie möglich abzubrechen, wenn er 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.
}

Obwohl diese Prüfung wird nicht in Ihrem Release-Build enden bewirkt, dass NullReferenceException erneut ausgelöst wird, wenn book == null zur Laufzeit im Release-Modus ausgeführt wird.

Verwenden Sie GetValueOrDefault() für nullfähige Werttypen, um einen Standardwert bereitzustellen, wenn sie null sind.

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

Verwenden Sie den Null-Koaleszenzoperator: ?? [C #] oder If() [VB].

Die Abkürzung zum Bereitstellen eines Standardwerts, wenn ein 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;
}

Verwenden Sie den Nullbedingungsoperator: ?. 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 für eine Eigenschaft mit einem Nullwert aufzurufen.

In C # 5 und darunter kann dies geschützt 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 dazu eine kürzere Syntax ein:

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

Dies führt dazu, dass die Titelvariable null ist und der Aufruf von ToUpper nicht erfolgt, wenn person.Titlenull ist.

Natürlich müssen Siestilltitle auf null prüfen oder den Operator null zusammen mit dem Operator null coalescing (??) 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] wie folgt 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, verhält es sich wie folgt: elem = myIntArray[i]; und gibt das i zurückth Element.

Verwenden Sie den Nullkontext (verfügbar in C # 8):

Eingeführt in C # 8 führen Nullkontexttypen und nullfähige Referenztypen statische Analysen für Variablen durch und geben eine Compilerwarnung aus, wenn ein Wert möglicherweise null sein kann oder auf null gesetzt wurde. Die nullfähigen Referenztypen ermöglichen es, dass Typen explizit die Erlaubnis erhalten, null zu sein.

Der nullfähige Anmerkungskontext und der nullfähige Warnkontext können für ein Projekt mithilfe des Nullable-Elements in Ihrer csproj-Datei festgelegt werden. Dieses Element konfiguriert, wie der Compiler die Nullwertfähigkeit von Typen interpretiert und welche Warnungen generiert werden. Gültige Einstellungen sind:

  • enable: Der nullfähige Anmerkungskontext ist aktiviert. Der nullfähige Warnkontext ist aktiviert. Variablen eines Referenztyps, z. B. eine Zeichenfolge, können nicht auf Null gesetzt werden. Alle Nullabilitätswarnungen sind aktiviert.
  • disable: Der nullfähige Anmerkungskontext ist deaktiviert. Der nullfähige Warnkontext ist deaktiviert. Variablen eines Referenztyps werden ebenso wie frühere Versionen von C # ignoriert. Alle Nullabilitätswarnungen sind deaktiviert.
  • safeonly: Der nullfähige Anmerkungskontext ist aktiviert. Der nullbare Warnkontext ist nur sicher. Variablen eines Referenztyps sind nicht stornierbar. Alle Sicherheitswarnungen zur Nichtigkeit sind aktiviert.
  • warnungen: Der Kontext für nullfähige Anmerkungen ist deaktiviert. Der nullfähige Warnkontext ist aktiviert. Variablen eines Referenztyps werden nicht berücksichtigt. Alle Nullabilitätswarnungen sind aktiviert.
  • safeonlywarnings: Der Kontext für nullfähige Anmerkungen ist deaktiviert. Der nullbare Warnkontext ist nur sicher. Variablen eines Referenztyps werden nicht berücksichtigt. Alle Sicherheitswarnungen zur Nichtigkeit sind aktiviert.

Ein nullwertfähiger Referenztyp wird mit der gleichen Syntax wie nullwertfähige Werttypen angegeben: Ein ? wird an den Typ der Variablen angehängt.

Spezielle Techniken zum Debuggen und Beheben von Null-Derefs in Iteratoren

C # unterstützt "Iteratorblöcke" (in einigen anderen gängigen Sprachen "Generatoren" genannt). NULL-Dereferenzierungsausnahmen können in Iteratorblöcken aufgrund der verzögerten Ausführung 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 zu null führt, wirft MakeFrob. 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? Denn der Iteratorblock führterst dannaus, wenn foreach! Der Aufruf von GetFrobs gibt einfach ein Objekt zurück, dasbei Iterationden Iteratorblock ausführt.

Indem Sie eine Nullprüfung wie diese schreiben, verhindern Sie die Null-Dereferenzierung, aber Sie verschieben die Nullargumentausnahme an den Punkt derIteration, nicht an den Punkt dercall, und das istsehr verwirrend zu debuggen.

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 dann wird GetFrobsForReal ausgeführt, wenn die Sequenz iteriert wird.

Wenn Sie die Referenzquelle für LINQ to Objects untersuchen, werden Sie feststellen, dass diese Technik durchgehend verwendet wird. Das Schreiben ist etwas umständlicher, aber das Debuggen von Nullitätsfehlern ist viel einfacher. Optimieren Sie Ihren Code zum Nutzen des Anrufers, nicht zum Nutzen des Autors.

Ein Hinweis zu Null-Dereferenzen in unsicherem Code

C # hat einen "unsicheren" Modus, der, wie der Name schon sagt, extrem gefährlich ist, da die normalen Sicherheitsmechanismen, die für Speichersicherheit und Typensicherheit sorgen, nicht erzwungen 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:

  • die Dereferenzierung einer Null Zeiger erzeugt die gleiche Ausnahme wie die Dereferenzierung einer Null Referenz
  • dereferenzierung eines ungültigen Nicht-Null-Zeigers kann unter bestimmten Umständen zu dieser Ausnahme führen

Um zu verstehen, warum das so ist, ist es hilfreich zu verstehen, wie .NET überhaupt keine Dereferenzierungsausnahmen erzeugt. (Diese Details gelten für .NET unter Windows. Andere Betriebssysteme verwenden ähnliche Mechanismen.)

Der Speicher wird in Windows virtualisiert. Jeder Prozess erhält einen virtuellen Speicherplatz mit vielen "Seiten" Speicher, die vom Betriebssystem überwacht werden. Auf jeder Speicherseite sind Flags gesetzt, die bestimmen, wie sie verwendet werden dürfen: Auslesen, Beschreiben, Ausführen usw. DieniedrigsteSeite wird als "Erzeugt einen Fehler, wenn jemals auf irgendeine Weise verwendet" 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 dann diesen Fehler und wandelt ihn in die Null-Dereferenzierungsausnahme um.

Aus diesem Grund führt die Dereferenzierung sowohl eines Nullzeigers als auch einer Nullreferenz zu derselben Ausnahme.

Was ist mit dem zweiten Punkt? Die Dereferenzierung vonjedemungültigen Zeiger, 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 einem nicht verwalteten Zeiger gleich null. Wenn wir versuchen, das zweite int in der Struktur zu dereferenzieren, versucht die CLR nicht, auf den Speicher an Position Null zuzugreifen. Es greift auf den Speicherplatz vier zu. Aber logischerweise ist dies eine Null-Dereferenzierung, weil wir zu dieser Adresseüberdie Null gelangen.

Wenn Sie mit unsicherem Code arbeiten und eine Null-Dereferenzierungsausnahme erhalten, beachten Sie, 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.

2304
John Saunders

NullReference-Ausnahme - Visual Basic

Der _NullReference Exception_ fürVisual Basicunterscheidet sich nicht von dem inC #. Immerhin melden beide dieselbe in .NET Framework definierte Ausnahme, die sie beide verwenden. In Visual Basic eindeutige Ursachen sind selten (möglicherweise nur eine).

Diese Antwort verwendet Visual Basic-Begriffe, -Syntax und -Kontext. Die verwendeten Beispiele stammen aus einer Vielzahl früherer Fragen zum Stapelüberlauf. Dies dient dazu, die Relevanz zu maximieren, indem die Arten von Situationen verwendet werden, die häufig in Posts auftreten. Für diejenigen, die es vielleicht brauchen, gibt es eine etwas ausführlichere Erklärung. Ein Beispiel, das Ihrem ähnlich ist, ist sehr wahrscheinlich hier aufgelistet.

Hinweis:

  1. Dies ist konzeptbasiert: Es gibt keinen Code, den Sie in Ihr Projekt einfügen können. Es soll Ihnen helfen zu verstehen, welche Ursachen ein NullReferenceException (NRE) hat, wie Sie es finden, wie Sie es beheben und wie Sie es vermeiden. Ein NRE kann auf viele Arten verursacht werden, sodass dies wahrscheinlich nicht Ihre einzige Begegnung ist.
  2. Die Beispiele (von Stapelüberlauf-Posts) zeigen nicht immer, wie man etwas am besten macht.
  3. In der Regel wird das einfachste Mittel angewendet.

Grundlegende Bedeutung

Die Meldung "Objekt wurde nicht auf eine Instanz von Object festgelegt" bedeutet, dass Sie versuchen, ein Objekt zu verwenden, das nicht initialisiert wurde. Daraus ergibt sich Folgendes:

  • Ihr Code deklariert ​​eine Objektvariable, aber es wurde nicht initialisiert ​​es (eine Instanz erstellen oder 'instanziieren' es)
  • Etwas, von dem Ihr Code annahm, dass es ein Objekt initialisieren würde, tat es nicht
  • Möglicherweise hat ein anderer Code ein noch verwendetes Objekt vorzeitig ungültig gemacht

Die Ursache finden

Da das Problem eine Objektreferenz ist, die Nothing ist, besteht die Antwort darin, sie zu untersuchen, um herauszufinden, welche. Bestimmen Sie dann, warum es nicht initialisiert wird. Halten Sie die Maus über die verschiedenen Variablen und Visual Studio (VS) zeigt ihre Werte an - der Schuldige ist Nothing.

IDE debug display

Sie sollten auch alle Try/Catch-Blöcke aus dem entsprechenden Code entfernen, insbesondere solche, in denen sich nichts im Catch-Block befindet. Dies führt zu einem Absturz des Codes, wenn versucht wird, ein Objekt zu verwenden, das Nothing ist.Dies ist, was Sie wollenweil es das genaue Ort ​​des Problems identifiziert und es Ihnen ermöglicht, das Objekt zu identifizieren, das es verursacht.

Ein MsgBox im Catch, der _Error while..._ anzeigt, wird wenig helfen. Diese Methode führt auch zu sehr schlecht ​​Fragen zum Stapelüberlauf, da Sie die tatsächliche Ausnahme, das betreffende Objekt oder sogar die Codezeile, in der sie auftritt, nicht beschreiben können.

Sie können auch den _Locals Window_ (Debug -> Windows -> Locals) verwenden, um Ihre Objekte zu untersuchen.

Sobald Sie wissen, wo und wo das Problem liegt, ist es in der Regel recht einfach und schneller zu beheben als das Posten einer neuen Frage.

Siehe auch:

Beispiele und Abhilfemaßnahmen

Klassenobjekte/Erstellen einer Instanz

_Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE
_

Das Problem ist, dass Dim kein CashRegister erstellt object; Es wird nur eine Variable mit dem Namen reg dieses Typs deklariert. Deklarieren einer Objektvariablen und Erstellen einer Instanz sind zwei verschiedene Dinge.

Abhilfe

Der Operator New kann häufig zum Erstellen der Instanz verwendet werden, 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 sinnvoll ist, die Instanz später zu erstellen:

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

Hinweis:Verwenden Sie Dim in einer Prozedur, einschließlich des Konstruktors (_Sub New_), nicht erneut:

_Private reg As CashRegister
'...

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

Dadurch wird die Variable localreg erstellt, die nur in diesem Kontext (sub) vorhanden ist. Die Variable reg mit der Modulebene Scope, die Sie überall sonst verwenden werden, bleibt Nothing.

Das Fehlen des Operators New ist die häufigste Ursache für _NullReference Exceptions_, die in den Stapelüberlauf-Fragen besprochen wurde.

Visual Basic versucht, den Prozess wiederholt mitNewzu verdeutlichen: Mit dem OperatorNewwird einnewobject und ruftSub New- den Konstruktor auf, in dem Ihr Objekt eine andere Initialisierung durchführen kann.

Um klar zu sein, nur Dim (oder Private) deklariert ​​eine Variable und ihre Type. Das Scope der Variablen - ob für das gesamte Modul/die gesamte Klasse vorhanden oder für eine Prozedur lokal - wird durch where deklariert. _Private | Friend | Public_ definiert die Zugriffsebene, nicht Scope.

Weitere Informationen finden Sie unter:


Arrays

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. Für Deklarationen auf Klassen-/Modulebene ist weiterhin _As <Type>_ mit _Option Strict_ erforderlich:

_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 von List(Of T) macht es ziemlich schwierig, ein Element ohne 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:


Listen und Sammlungen

.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 Type verwendet:

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

Beide Prozeduren führen zu einem NRE, da barList nur deklariert und nicht instanziiert wird. Beim Erstellen einer Instanz von Foo wird auch keine Instanz von barList erstellt. Möglicherweise war es 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) Class .


Datenproviderobjekte

Das Arbeiten mit Datenbanken bietet viele Möglichkeiten für eine NullReference, da viele Objekte (Command, Connection, Transaction, Dataset, DataTable, DataRows....) gleichzeitig verwendet werden können.Hinweis:Es spielt keine Rolle, welchen Datenprovider Sie verwenden - MySQL, SQL Server, OleDB usw. - die Konzepte sind die gleich.

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
_

Wie zuvor wurde das Dataset-Objekt ds deklariert, es wurde jedoch nie eine Instanz erstellt. Der DataAdapter füllt einen vorhandenen DataSet und erstellt keinen. In diesem Fall, da ds eine lokale Variable ist, warnt die IDE Sie, dass dies passieren könnte:

img

Wenn der Compiler als Modul-/Klassenvariable deklariert ist, wie es bei con der Fall zu sein scheint, kann er nicht wissen, ob das Objekt durch eine vorgelagerte Prozedur erstellt wurde. Ignorieren Sie keine Warnungen.

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 ist hier ein Problem: Employees vs Employee. Es wurde kein DataTable mit dem Namen "Employee" erstellt, sodass ein NullReferenceException-Ergebnis beim Versuch, darauf zuzugreifen, angezeigt wird. Ein weiteres mögliches Problem ist die Annahme, dass es Items gibt, was möglicherweise nicht der Fall ist, wenn die SQL eine WHERE-Klausel enthält.

Abhilfe

Da dies eine Tabelle verwendet, werden durch die Verwendung von Tables(0) Rechtschreibfehler vermieden. Das Untersuchen von _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 betroffenen Rows 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
_

DataAdapter liefert TableNames, wie im vorherigen Beispiel gezeigt, analysiert jedoch keine Namen aus der SQL- oder Datenbanktabelle. Daher verweist ds.Tables("TICKET_RESERVATION") auf eine nicht vorhandene Tabelle.

DasRemedyist dasselbe, referenzieren Sie die Tabelle nach Index:

_If ds.Tables(0).Rows.Count > 0 Then
_

Siehe auch DataTable-Klasse .


Objektpfade/Verschachtelt

_If myFoo.Bar.Items IsNot Nothing Then
   ...
_

Der Code testet nur Items, während sowohl myFoo als auch Bar auch Nothing sein können. Mitremedykönnen Sie die gesamte Kette oder den gesamten Pfad von Objekten einzeln testen:

_If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....
_

AndAlso ist wichtig. Nachfolgende Tests werden nicht ausgeführt, sobald die erste False-Bedingung vorliegt. Auf diese Weise kann der Code sicher eine Ebene nach der anderen in die Objekte "bohren" und _myFoo.Bar_ nur dann auswerten, wenn (und wenn) myFoo als gültig ermittelt wurde. 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, auf ein null-Objekt 'nachgeordnet' zu verweisen. Dies gilt auch für Kontrollen:

_myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
_

Hier könnte myWebBrowser oder Document Nothing sein, oder das _formfld1_ -Element ist möglicherweise nicht vorhanden.


UI-Steuerelemente

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

Unter anderem geht dieser Code nicht davon aus, dass der Benutzer in einem oder mehreren Steuerelementen der Benutzeroberfläche möglicherweise nichts ausgewählt hat. _ListBox1.SelectedItem_ kann durchaus Nothing sein, daher führt _ListBox1.SelectedItem.ToString_ zu einem NRE.

Abhilfe

Überprüfen Sie die Daten, bevor Sie sie verwenden (verwenden Sie auch die Parameter _Option Strict_ und 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
_

Alternativ können Sie _(ComboBox5.SelectedItem IsNot Nothing) AndAlso..._ verwenden.


Visual Basic-Formulare

_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 recht gebräuchlicher Weg, um ein NRE zu erhalten. In C # meldet das IDE je nach Codierung, dass Controls im aktuellen Kontext nicht vorhanden ist oder "nicht auf nicht statische Member verweisen kann". In gewissem Maße handelt es sich also um eine reine VB-Situation. Es ist auch komplex, da es zu einer Ausfallkaskade kommen kann.

Die Arrays und Auflistungen können nicht auf diese Weise initialisiert werden. Dieser Initialisierungscode wird ausgeführt bevor der Konstruktor das Form oder das Controls erstellt. Als Ergebnis:

  • Listen und Sammlungen sind einfach leer
  • Das Array enthält fünf Elemente von Nothing
  • Die somevar-Zuweisung führt zu einem sofortigen NRE, da Nothing keine _.Text_ -Eigenschaft besitzt

Wenn Sie später auf Array-Elemente verweisen, wird ein NRE erstellt. Wenn Sie dies in _Form_Load_ tun, meldet das IDE möglicherweise nicht ​​die Ausnahme, wenn sie auftritt. Die Ausnahme wird später angezeigt, wenn Ihr Code versucht, das Array zu verwenden. Diese "stille Ausnahme" ist ausführlich in diesem Beitrag . Für unsere Zwecke ist der Schlüssel, dass, wenn beim Erstellen eines Formulars (_Sub New_ oder _Form Load_) etwas Katastrophales passiert, Ausnahmen möglicherweise nicht gemeldet werden, der Code die Prozedur verlässt und nur das Formular anzeigt.

Da kein anderer Code in Ihrem Ereignis _Sub New_ oder _Form Load_ nach dem NRE ausgeführt wird, kann 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
_

HinweisDies gilt für alle Verweise auf Steuerelemente und Komponenten, die diese unzulässig machen, sofern sie:

_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 ausgibt, aber das Mittel ist,die Container auf Formularebene zu deklarieren, aberinitialisierensie in der Ereignisbehandlungsroutine für das Laden von Formularen, wenn die Steuerelementedovorhanden sind. Dies kann in _Sub New_ erfolgen, solange sich Ihr Code nach dem Aufruf von InitializeComponent 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 (z. B. GroupBox oder Panel), werden in _Me.Controls_ nicht gefunden. Sie befinden sich in der Controls-Auflistung dieses Panels oder dieser GroupBox. Es wird auch kein Steuerelement zurückgegeben, wenn der Name des Steuerelements falsch geschrieben ist (_"TeStBox2"_). In solchen Fällen wird Nothing erneut in diesen Array-Elementen gespeichert, und es wird eine NRE ausgegeben, wenn Sie versuchen, darauf zu verweisen.

Diese sollten jetzt, da Sie wissen, wonach Sie suchen, leicht zu finden sein: VS shows you the error of your ways

"Button2" befindet sich auf einem Panel

Abhilfe

Verwenden Sie die Steuerelementreferenz anstelle von indirekten Verweisen nach Namen, die die Controls-Auflistung des Formulars verwenden:

_' 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...})
_

Funktion, die nichts zurückgibt

_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 IDE Sie warnt, dass 'nicht alle Pfade einen Wert zurückgeben und ein NullReferenceException resultieren kann'. Sie können die Warnung unterdrücken, indem Sie _Exit Function_ durch _Return Nothing_ ersetzen, aber das löst das Problem nicht. Alles, was versucht, die Rückgabe zu verwenden, wenn _someCondition = False_ zu einem NRE führt:

_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 leerList ist nicht dasselbe wie das Zurückgeben von Nothing. Wenn die Möglichkeit besteht, dass ein zurückgegebenes Objekt Nothing sein kann, testen Sie es, bevor Sie es verwenden:

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

Schlecht implementiertes Try/Catch

Ein schlecht implementiertes Try/Catch kann das Problem verbergen 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 der Fall, wenn ein Objekt nicht wie erwartet erstellt wird, zeigt aber auch die Nützlichkeit eines leeren Catch.

Es gibt ein zusätzliches Komma in der SQL (nach 'mailaddress'), was zu einer Ausnahme bei _.ExecuteReader_ führt. Nachdem Catch nichts unternommen hat, versucht Finally, eine Bereinigung durchzuführen. Da Sie jedoch kein Close -Null-Objekt DataReader erstellen können, wird ein brandneues NullReferenceException -Ergebnis angezeigt.

Ein leerer Catch Block ist der Spielplatz des Teufels. Dieses OP war verblüfft, warum er ein NRE im Block Finally bekam. In anderen Situationen kann ein leeres Catch dazu führen, dass etwas anderes viel weiter stromabwärts verdrahtet wird und Sie Zeit damit verbringen, die falschen Dinge am falschen Ort nach dem Problem zu durchsuchen. (Die oben beschriebene "stille Ausnahme" bietet denselben 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 dienen nicht dazu, Ausnahmen vor der Person zu verbergen, die für deren Behebung ausschließlich qualifiziert ist - dem Entwickler.


DBNull ist nicht dasselbe wie Nothing

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

Die Funktion IsDBNull wird verwendet, um zu testen, ob ein Wert ​​_System.DBNull_: Von MSDN: entspricht

Der System.DBNull-Wert 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, nämlich Nothing für Referenztypen und niemals DBNull:

_If getFoo IsNot Nothing Then...
_

Kontrollen

_Dim chk As CheckBox

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

Wenn ein CheckBox mit chkName nicht gefunden werden kann (oder in einem GroupBox vorhanden ist), ist chk Nothing und der Versuch, auf eine Eigenschaft zu verweisen, führt zu einer Ausnahme.

Abhilfe

_If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
_

Die DataGridView

Die DGV hat ein paar Macken in regelmäßigen Abständen zu sehen:

_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 dgvBooks _AutoGenerateColumns = True_ hat, werden die Spalten erstellt, jedoch nicht benannt, sodass der obige Code fehlschlägt, wenn auf sie namentlich verwiesen wird.

Abhilfe

Benennen Sie die Spalten manuell oder referenzieren Sie sie nach Index:

_dgvBooks.Columns(0).Visible = True
_

Beispiel 2 - Vorsicht vor der NewRow

_xlWorkSheet = xlWorkBook.Sheets("sheet1")

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

Wenn Ihr DataGridViewAllowUserToAddRows als True hat (Standardeinstellung), enthält das Cells in der leeren/neuen Zeile unten alle Nothing. Die meisten Versuche, den Inhalt zu verwenden (z. B. ToString), führen zu einem NRE.

Abhilfe

Verwenden Sie eine _For/Each_-Schleife, und testen Sie die Eigenschaft IsNewRow, um festzustellen, ob es sich um die letzte Zeile handelt. Dies funktioniert unabhängig davon, ob AllowUserToAddRows 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 wahr ist.


My.Settings (StringCollection)

Unter bestimmten Umständen kann der Versuch, ein Element aus _My.Settings_ zu verwenden, bei dem es sich um ein StringCollection handelt, zu einer NullReference führen, wenn Sie es zum ersten Mal verwenden. Die Lösung ist die gleiche, 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 der Fall, 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 Nothing, 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-Auflistung nur beim ersten Ausführen der Anwendung initialisiert werden. Alternativ können Sie Ihrer Sammlung unterProjekt -> Einstellungen | einen Anfangswert hinzufügen FooBars, speichern Sie das Projekt und entfernen Sie den falschen Wert.


Wichtige Punkte

Sie haben wahrscheinlich den Operator New vergessen.

oder

Etwas, von dem Sie angenommen haben, dass es fehlerfrei funktioniert, wenn ein initialisiertes Objekt an Ihren Code zurückgegeben wird, hat dies nicht getan.

Ignorieren Sie keine Compiler-Warnungen (jemals) und verwenden Sie _Option Strict On_ (immer).


MSDN NullReference Exception

Ein anderes Szenario ist, wenn Sie ein Nullobjekt in ein Werttyp umwandeln. Zum Beispiel der folgende Code:

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

Es wird ein NullReferenceException auf die Besetzung werfen. In dem obigen Beispiel scheint dies ziemlich offensichtlich zu sein, aber dies kann in komplizierteren Szenarien mit "später Bindung" auftreten, 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 das folgende einfache ASP.NET-Bindungsfragment mit dem Calendar-Steuerelement:

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

Hier ist SelectedDate tatsächlich eine Eigenschaft des Typs DateTime des Typs Calendar Web Control, und die Bindung könnte perfekterweise etwas Null zurückgeben. Der implizite ASP.NET-Generator erstellt einen Code, der dem obigen Umwandlungscode entspricht. Und dies wird ein NullReferenceException erzeugen, das ziemlich schwer zu erkennen ist, da es in ASP.NET-generiertem Code liegt, der sich gut kompilieren lässt ...

224
Simon Mourier

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, da ich zwar die Variable "connection" deklariert habe, sie aber nicht auf irgendetwas zeigt. Wenn ich versuche, das Member "Open" aufzurufen, muss es nicht aufgelöst werden, und es wird den Fehler auslösen.

Um diesen Fehler zu vermeiden:

  1. Initialisieren Sie Ihre Objekte immer, bevor Sie versuchen, etwas mit ihnen zu tun.
  2. Wenn Sie nicht sicher sind, ob das Objekt null ist, überprüfen Sie es mit object == null.

Das JetBrains-Resharper-Tool erkennt jede Stelle in Ihrem Code, an der möglicherweise ein Null-Referenzfehler auftritt, und ermöglicht es Ihnen, eine Null-Überprüfung durchzuführen. Dieser Fehler ist die häufigste Fehlerquelle, IMHO.

156

Dies bedeutet, dass Ihr Code eine Objektreferenzvariable verwendet hat, die auf null gesetzt wurde (d. H., Dass sie nicht auf eine tatsächliche Objektinstanz verwiesen 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
}
153
Jonathan Wood

Beachten Sie, dass die Ursache in .NET unabhängig vom Szenario immer dieselbe ist:

Sie versuchen, eine Referenzvariable zu verwenden, deren Wert Nothing/null ist. Wenn der Wert für die Referenzvariable Nothing/null 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 Sie haben die Variable manuell auf Nothing/null gesetzt, oder Sie haben eine Funktion aufgerufen, mit der die Variable festgelegt wurde an Nothing/null für Sie.

95
code master

Ein Beispiel für diese Ausnahmebedingung lautet: 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, d. H. Für den obigen Code.

Im Vergleich zu einer ArgumentNullException, die normalerweise als defensive Maß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.

86
Alex KeySmith

Update C # 8.0, 2019: Nullable reference types

C # 8.0 führt nullfähige Referenztypen und nicht nullfähige Referenztypen ein. Daher müssen nur nullfähige Referenztypen überprüft werden, um eine NullReferenceException zu vermeiden.


Wenn Sie einen Referenztyp nicht 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 prüfen, ob die Variable nicht null ist:

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

Um zu verstehen, warum eine NullReferenceException ausgelöst wird, ist es wichtig, den Unterschied zwischen Werttypen und [Referenztypen] [3] zu kennen.

Wenn Sie es also mit Werttypen zu tun haben, können NullReferenceExceptions nicht auftreten. Sie müssen jedoch im Umgang mit Referenztypen wachsam sein!

Nur Referenztypen können, wie der Name schon sagt, Referenzen enthalten oder wörtlich auf nichts verweisen (oder 'null'). Während Werttypen immer einen Wert enthalten.

Referenztypen (diese müssen überprüft werden):

  • dynamisch
  • objekt
  • zeichenfolge

Werttypen (Sie können diese einfach ignorieren):

  • Numerische Typen
  • Integrale Typen
  • Gleitkommatypen
  • dezimal
  • scheiße
  • Benutzerdefinierte Strukturen
85
Fabian Bigler

Ein weiterer Fall, in dem NullReferenceExceptions auftreten kann, ist die (falsche) Verwendung des Operators as :

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 sind Book und Car inkompatible Typen. Ein Car kann nicht in ein Book konvertiert/umgewandelt werden. Wenn diese Umwandlung fehlschlägt, gibt asnull zurück. Die anschließende Verwendung von mybook führt zu einem NullReferenceException.

Im Allgemeinen sollten Sie eine Besetzung oder as wie folgt verwenden:

Wenn Sie erwarten, dass die Typkonvertierung immer erfolgreich ist (dh Sie wissen, was das Objekt vor der Zeit sein sollte), sollten Sie eine Umwandlung verwenden:

ComicBook cb = (ComicBook)specificBook;

Wenn Sie sich nicht sicher sind, aber den Typ try als bestimmten Typ verwenden möchten, verwenden Sie as:

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

Sie verwenden das Objekt, das die Nullwertreferenz enthält. Es gibt also eine Null-Ausnahme. In diesem Beispiel ist der Zeichenfolgewert null, und bei der Überprüfung der 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: Der Objektverweis wurde nicht auf eine Instanz eines Objekts festgelegt. bei Program.Main ()

64
user1814380

Während , was eine NullReferenceExceptions und Ansätze zu vermeiden/fix Eine solche Ausnahme wurde in anderen Antworten angesprochen. Was viele Programmierer noch nicht gelernt haben, ist, wie sie solche Ausnahmen während der Entwicklung unabhängig debuggen .

In Visual Studio ist dies normalerweise dank des Visual Studio Debugger einfach.


Stellen Sie zunächst sicher, dass der richtige Fehler abgefangen wird - siehe Wie kann ich zulassen, dass 'System.NullReferenceException' in VS2010 unterbrochen wird? Hinweis1

Dann entweder Start with Debugging (F5) oder Attach [the VS Debugger] zu Running Process . Gelegentlich kann es nützlich sein, Debugger.Break zu verwenden, um den Debugger zu starten.

Wenn die NullReferenceException ausgelöst (oder nicht behandelt) wird, stoppt der Debugger in der Zeile, in der die Ausnahme aufgetreten ist (beachten Sie die oben festgelegte Regel?). Manchmal ist der Fehler leicht zu erkennen.

In der folgenden Zeile ist beispielsweise der einzige Code, der die Ausnahme verursachen kann , wenn myString den Wert null hat. Dies kann durch Betrachten des Watch Window oder Ausführen von Ausdrücken im Immediate Window überprüft werden.

_var x = myString.Trim();
_

In fortgeschritteneren Fällen, wie z. B. den folgenden, müssen Sie eine der oben genannten Techniken (Überwachen oder Sofortiges Windows) verwenden, um die Ausdrücke zu überprüfen und festzustellen, ob _str1_ null war oder ob _str2_ null war .

_var x = str1.Trim() + str2.Trim();
_

Sobald wo die Ausnahme gefunden wurde, ist es normalerweise trivial, rückwärts zu argumentieren, um herauszufinden, wo der Nullwert [falsch] eingeführt wurde -

Nehmen Sie sich die Zeit, um die Ursache der Ausnahme zu verstehen. Untersuchen Sie auf Null-Ausdrücke. Überprüfen Sie die vorherigen Ausdrücke, die zu solchen Null-Ausdrücken hätten führen können. Fügen Sie Haltepunkte hinzu und führen Sie die entsprechenden Schritte 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 erfassten Ausnahmen zu begrenzen. Zusätzlich führt VS2012 Just My Code ein, das ich ebenfalls empfehlen würde.

Wenn Sie das Debuggen mit aktivierter Option "Nur mein Code" ausführen, ist das Verhalten etwas anders. Wenn "Nur mein Code" aktiviert ist, ignoriert der Debugger CLR-Ausnahmen (Common Language Runtime) der ersten Chance, die außerhalb von "Mein Code" ausgelöst werden und nicht über "Mein Code" übertragen werden

63
user2864740

Simon Mourier gab dieses Beispiel :

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

wobei eine Unboxing Konvertierung (Besetzung) von object (oder aus einer der Klassen System.ValueType oder System.Enum oder aus einem Schnittstellentyp) bis einen Werttyp (außer Nullable<>) an sich gibt den NullReferenceException.

In der anderen Richtung ergibt sich eine Boxen Konvertierung von Ein Nullable<>, der HasValue gleich false zu einem Referenztyp hat, kann einen null -Referenz geben, der dann kann später zu einem NullReferenceException führen. 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 wird problematisch sein:

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.

57

Hinzufügen eines Falls, bei dem der Klassenname für die in Entity Framework verwendete Entität mit dem Klassennamen für eine CodeBehind-Datei für Webformulare 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 die Entität und die Codebehind-Klasse im gleichen Namespace befinden. Benennen Sie die Entitätsklasse oder die Codebehind-Klasse für Contact.aspx um, um das zu beheben.

Grund Ich bin mir immer noch nicht sicher über den Grund. Dieser Fehler tritt jedoch immer dann auf, wenn eine der Entitätsklassen System.Web.UI.Page erweitert.

Zur Diskussion schauen Sie sich NullReferenceException in DbContext.saveChanges () an

41
AbhinavRanjan

Ein weiterer allgemeiner Fall, in dem diese Ausnahme auftreten kann, ist das Verspotten von Klassen während des Komponententests. Unabhängig davon, welches Mock-Framework verwendet wird, müssen Sie sicherstellen, dass alle geeigneten Ebenen der Klassenhierarchie ordnungsgemäß verspottet sind. Insbesondere müssen alle Eigenschaften von HttpContext, auf die der zu testende Code verweist, verspottet werden.

Ein etwas ausführliches Beispiel finden Sie unter " NullReferenceException ausgelöst beim Testen von benutzerdefiniertem AuthorizationAttribute ".

40
John Saunders

Ich habe eine andere Perspektive, um darauf zu antworten. Diese Art von Antworten "Was kann ich noch tun, um dies zu vermeiden?"

Wenn Sie über mehrere Ebenen hinweg arbeiten, beispielsweise in einer MVC-Anwendung, benötigt ein Controller Dienste, um Geschäftsvorgänge aufzurufen. In solchen Szenarien kann Dependency Injection Container verwendet werden, um die Dienste zu initialisieren und die NullReferenceException zu vermeiden. Das bedeutet, dass Sie sich keine Gedanken über das Überprüfen auf Null machen müssen und die Dienste nur 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();
    }
}
39
Mukus

In Bezug auf "Was soll ich dagegen tun" kann es viele Antworten geben.

Eine "formalere" Möglichkeit, solche Fehlerzuständewährend der Entwicklung zu verhindern, besteht darin,Design by Contractin Ihrem Code. Das heißt, Sie müssen die Klasseinvariantsund/oder sogar die Funktion/methodpreconditionsundpostconditionsauf Ihrem System während der Entwicklung.

Kurz gesagt,Klasseninvariantenstellen sicher, dass es in Ihrer Klasse einige Einschränkungen gibt, die bei normaler Verwendung nicht verletzt werden (und daher wird die Klassenotin einen inkonsistenten Zustand geraten).Vorbedingungenbedeutet, dass Daten, die als Eingabe für eine Funktion/Methode angegeben werden, bestimmten Einschränkungen folgen müssen und nie sie verletzen müssen, undpostconditionsbedeutet, dass eine Funktions-/Methodenausgabe den festgelegten Einschränkungen erneut folgen muss, ohne sie jemals zu verletzen. Die Vertragsbedingungen sollten nie während der Ausführung eines fehlerfreien Programms verletzt werden, daher wird das vertragliche Design in der Praxis im Debug-Modus überprüft, während in Releasesdeaktiviert ist, 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, und X 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 "Eigenschaft X darf niemals einen Nullwert haben" als Methodenvoraussetzung festlegen, können Sie das zuvor beschriebene Szenario verhindern:

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant () 
{
    Contract.Invariant ( X != null );
    //...
}

Aus diesem Grund ist für .NET-AnwendungenCode Contractsproject vorhanden.

Alternativ kann Design by Contract mitZusicherungenangewendet werden.

PDATE: Erwähnenswert ist, dass der Begriff von Bertrand Meyer geprägt wurde im Zusammenhang mit seinem Entwurf der Programmiersprache Eiffel .

37

Ein NullReferenceException wird ausgelöst, wenn versucht wird, auf Eigenschaften eines Nullobjekts zuzugreifen, oder wenn ein Zeichenfolgenwert leer wird und versucht wird, auf Zeichenfolgenmethoden zuzugreifen.

Zum Beispiel:

  1. Wenn auf eine Zeichenfolgemethode einer leeren Zeichenfolge zugegriffen wird:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
    
  2. Wenn auf eine Eigenschaft eines Nullobjekts zugegriffen wird:

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 
    
35
Hemant Bavle

TL; DR: Versuchen Sie, Html.Partial anstelle von Renderpage zu verwenden.


Ich habe Object reference not set to an instance of an object erhalten, als ich versucht habe, eine Ansicht in einer Ansicht zu rendern, indem ich ihr ein Modell 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 in:

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);

Und es hat funktioniert.

Der Grund, warum ich Html.Partial nicht hatte, war, dass Visual Studio manchmal fehlerhaft aussehende verschnörkelte Linien unter Html.Partial wirft, 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 beheben, indem ich die Struktur der foreach-Schleife so änderte, dass sie wie folgt aussah:

@foreach(var M in MyEntities){
    ...
}

Obwohl ich das Gefühl habe, dass Visual Studio das kaufmännische Und und die eckigen Klammern falsch interpretiert hat.

30
Travis Heeter

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, das Problem zu verhindern oder es zumindest einfacher zu machen, es zu fangen.

Argumente prüfen

Beispielsweise können Methoden die verschiedenen Argumente überprüfen, um festzustellen, ob sie null sind, und eine ArgumentNullException auslösen, eine Ausnahme, die offensichtlich genau für diesen Zweck erstellt wurde.

Der Konstruktor für ArgumentNullException verwendet sogar den Namen des Parameters und eine Nachricht als Argumente, damit Sie dem Entwickler genau mitteilen können, um welches Problem es sich handelt.

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

Tools verwenden

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 Contract.Requires(obj != null) verwenden, mit der Sie die Laufzeit und Kompilierung überprüfen können: Introducing Code Contracts .

Es gibt auch "PostSharp", mit dem Sie Attribute wie diese verwenden können:

public void DoSometing([NotNull] obj)

Auf diese Weise wird obj zur Laufzeit auf null überprüft, damit PostSharp Teil Ihres Build-Prozesses wird. Siehe: PostSharp null check

Plain Code Solution

Oder Sie können Ihren eigenen Ansatz immer mit einfachem altem Code codieren. Hier ist beispielsweise eine Struktur, mit der Sie Nullreferenzen abfangen können. Es ist nach dem gleichen Konzept wie Nullable<T> modelliert:

[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 verwenden wie Nullable<T>, außer mit dem Ziel, genau das Gegenteil zu erreichen - null nicht zuzulassen. 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 in und aus T umgewandelt, sodass Sie es praktisch überall verwenden können, wo Sie es benötigen. Beispielsweise können Sie ein Person-Objekt an eine Methode übergeben, die einen NotNull<Person> annimmt:

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

Wie Sie oben sehen können, würden Sie wie bei nullable über die Eigenschaft Value auf den zugrunde liegenden Wert zugreifen. Alternativ können Sie eine explizite oder implizite Umwandlung verwenden. Nachfolgend sehen Sie ein Beispiel mit dem Rückgabewert:

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

Sie können es auch verwenden, wenn die Methode nur T (in diesem Fall Person) zurückgibt, indem Sie eine Umwandlung durchführen. Der folgende Code würde beispielsweise den obigen Code genau mögen:

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

Mit Erweiterung kombinieren

Wenn Sie NotNull<T> mit einer Erweiterungsmethode kombinieren, können Sie 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 null ist, gibt der gesamte Ausdruck null zurück.

Dies reduziert die Anzahl der Nullprüfungen, die Sie in einigen Fällen durchführen müssen. Die Syntax besteht darin, vor jeden Punkt ein Fragezeichen zu setzen. Nehmen Sie zum Beispiel den folgenden Code:

var address = country?.State?.County?.City;

Stellen Sie sich vor, country ist ein Objekt vom Typ Country mit der Eigenschaft State und so weiter. Wenn country, State, County oder Citynull ist, dann address will benull. Therefore you only have to check whetheraddressisnull`.

Es ist eine großartige Funktion, aber es gibt Ihnen weniger Informationen. Es ist nicht ersichtlich, 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 nach dem Typ ein Fragezeichen setzen wie int?.

Es wäre schön, wenn C # so etwas wie die obige NotNull<T> -Struktur und eine ähnliche Kurzform hätte, vielleicht das Ausrufezeichen (!), Damit Sie so etwas schreiben könnten wie: public void WriteName(Person! person).

22
Luis Perez
9
M.Hassan

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: Nehmen wir an, Sie haben eine Klasse namens myClass und sie enthält eine Eigenschaft prop1.

public Class myClass
{
   public int prop1 {get;set;}
}

Jetzt greifen Sie auf dieses prop1 in einer anderen Klasse wie unten 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;  
     }
}
8
Jaimin Dave

Interessanterweise erwähnt keine der Antworten auf dieser Seite die beiden Edge-Fälle. Hoffe, dass es niemanden stört, wenn ich sie hinzufüge:

Randfall 1: gleichzeitiger Zugriff auf ein Wörterbuch

Generische Wörterbücher in .NET sind nicht threadsicher und können manchmal ein NullReference oder sogar (häufiger) ein KeyNotFoundException auslösen, wenn Sie versuchen, von zwei gleichzeitig auf einen Schlüssel zuzugreifen Fäden. Die Ausnahme ist in diesem Fall ziemlich irreführend.

Randfall Nr. 2: unsicherer Code

Wenn ein NullReferenceException durch unsafe Code ausgelöst wird, können Sie Ihre Zeigervariablen überprüfen und sie auf IntPtr.Zero oder etwas anderes überprüfen. Das ist dasselbe ("Nullzeiger-Ausnahme"), aber in unsicherem Code werden Variablen häufig in Werttypen/Arrays usw. umgewandelt, und Sie stoßen mit dem Kopf gegen die Wand und fragen sich, wie ein Werttyp dies auslösen kann Ausnahme.

(Ein weiterer Grund, warum Sie unsicheren Code nicht verwenden, es sei denn, Sie benötigen ihn.)

8
jazzcat

Eine 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 mit dem Namen 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 also die Anweisung s.GetFullName () ausgeführt wird, wird die NullReferenceException ausgelöst.

3
Nick

Nun, in einfachen Worten:

Sie versuchen, auf ein Objekt zuzugreifen, das nicht erstellt wurde oder sich derzeit nicht im Speicher befindet.

So gehen Sie vor:

  1. Debuggen und den Debugger unterbrechen lassen ... Sie werden direkt zu der fehlerhaften Variablen weitergeleitet ... Jetzt müssen Sie das einfach beheben. Verwenden Sie das neue Schlüsselwort in der geeignete Ort.

  2. Wenn es bei einigen Datenbankbefehlen auftritt, weil das Objekt nicht vorhanden ist, müssen Sie nur eine Nullprüfung durchführen und damit umgehen:

    if (i == null) {
        // Handle this
    }
    
  3. Das Schwierigste .. wenn GC das Objekt bereits gesammelt hat ... Dies tritt im Allgemeinen auf, wenn Sie versuchen, ein Objekt mithilfe von Zeichenfolgen zu finden. .. Das heißt, wenn Sie es nach dem Namen des Objekts suchen, kann es vorkommen, dass der GC es bereits bereinigt hat. Dies ist schwer zu finden und wird zu einem ziemlichen Problem Überprüfungen, wo immer dies während des Entwicklungsprozesses erforderlich ist. So sparen Sie viel Zeit.

Mit dem Suchen nach Namen meine ich, dass einige Frameworks es Ihnen ermöglichen, Objekte mithilfe von Zeichenfolgen zu finden, und der Code könnte folgendermaßen aussehen: FindObject ("ObjectName");

2
Akash Gutha

Es gibt zwei Möglichkeiten, um eine NullReferenceExeption im wahrsten Sinne des Wortes zu beheben. Wenn Sie beispielsweise ein GameObject mit einem angehängten Skript und einer Variablen mit dem Namen rb (starrer Körper) haben, wird diese Variable beim Starten des Spiels mit null beginnen.
Aus diesem Grund erhalten Sie eine NullReferenceExeption, da auf dem Computer keine Daten in dieser Variablen gespeichert sind.

Als Beispiel verwende ich eine RigidBody-Variable.
Wir können Daten auf verschiedene Arten ganz einfach hinzufügen:

  1. Fügen Sie Ihrem Objekt mit AddComponent> Physics> Rigidbody einen RigidBody hinzu
    Dann gehe in dein Skript und tippe rb = GetComponent<Rigidbody>();
    Diese Codezeile funktioniert am besten mit Ihren Funktionen Start() oder Awake().
  2. Sie können eine Komponente programmgesteuert hinzufügen und die Variable gleichzeitig mit einer Codezeile zuweisen: 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 eingeben (das Leerzeichen unter allen Ihren Verwendungen).
Viel Spaß beim Spielen!

0
CausticLasagne

Wenn wir allgemeine Szenarien betrachten, in denen diese Ausnahme ausgelöst werden kann, greifen Sie auf Eigenschaften zu, wobei sich das Objekt oben befindet.

Ex:

string postalcode=Customer.Address.PostalCode; 
//if customer or address is null , this will through exeption

wenn die Adresse hier null ist, erhalten Sie eine NullReferenceException.

In der Praxis sollten wir daher immer die Nullprüfung verwenden, bevor wir auf Eigenschaften in solchen Objekten zugreifen (insbesondere in generischen Objekten).

string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
0
Hiran