Vraag Wat is een NullReferenceException en hoe los ik dit op?


Ik heb wat code en wanneer het wordt uitgevoerd, gooit het een NullReferenceException, zeggend:

Objectreferentie niet ingesteld op een exemplaar van een object.

Wat betekent dit en wat kan ik doen om deze fout te verhelpen?


1878


oorsprong


antwoorden:


Wat is de oorzaak?

Bottom Line

Je probeert iets te gebruiken dat is null (of Nothing in VB.NET). Dit betekent dat je het hebt ingesteld op nullof je hebt het helemaal nooit ingesteld.

Zoals al het andere, null wordt doorgegeven. Als het is null  in methode "A", kan het zijn dat methode "B" a gepasseerd is null  naar methode "A".

null kan verschillende betekenissen hebben:

  1. Objectvariabelen die zijn uninitialized en daarom wijst naar niets. Als u in dit geval de eigenschappen of methoden van dergelijke objecten gebruikt, veroorzaakt dit een NullReferenceException.
  2. De ontwikkelaar is gebruik makend van null opzettelijk om aan te geven dat er geen zinvolle waarde beschikbaar is. Merk op dat C # het concept van nullable datatypes voor variabelen heeft (zoals databasetabellen kunnen null-vrije velden hebben) - u kunt toewijzen null om aan te geven dat er geen waarde is opgeslagen, bijvoorbeeld int? a = null; waar het vraagteken aangeeft dat het toegestaan ​​is om null in variabele op te slaan a. Je kunt dat controleren met of if (a.HasValue) {...} of met if (a==null) {...}. Nullable variabelen, zoals a laat dit voorbeeld toe om de waarde via te benaderen a.Value expliciet of gewoon zoals normaal via a.
    Notitie die toegang via a.Value gooit een InvalidOperationException inplaats van een NullReferenceException als a is null - u moet van tevoren de controle uitvoeren, d.w.z. als u nog een niet-nul-variabele hebt int b; dan zou je opdrachten moeten doen zoals if (a.HasValue) { b = a.Value; } of korter if (a != null) { b = a; }.

De rest van dit artikel gaat in meer detail en toont fouten die veel programmeurs vaak maken die kunnen leiden tot een NullReferenceException.

Specifieker

De runtime gooit een NullReferenceException  altijd betekent hetzelfde: u probeert een referentie te gebruiken en de referentie is niet geïnitialiseerd (of het was een keer geïnitialiseerd, maar is niet langer begonnen).

Dit betekent dat de referentie is null, en je hebt geen toegang tot leden (zoals methoden) via a null referentie. Het eenvoudigste geval:

string foo = null;
foo.ToUpper();

Dit zal een a gooien NullReferenceException op de tweede regel omdat u de instantiemethode niet kunt aanroepen ToUpper() op een string verwijzing naar null.

debugging

Hoe vind je de bron van een NullReferenceException? Afgezien van het kijken naar de uitzondering zelf, die precies op de locatie wordt gegooid waar deze optreedt, gelden de algemene regels voor foutopsporing in Visual Studio: plaats strategische breekpunten en inspecteer uw variabelen, door de muis over hun naam te houden, een (snel) kijkvenster te openen of de verschillende foutopsporingspanelen zoals Locals en Autos te gebruiken.

Als u wilt weten waar de referentie is of niet is ingesteld, klikt u met de rechtermuisknop op de naam en selecteert u 'Alle referenties zoeken'. U kunt vervolgens op elke gevonden locatie een onderbrekingspunt plaatsen en uw programma uitvoeren met de debugger als bijlage. Telkens wanneer de foutopsporing breekt op een dergelijk onderbrekingspunt, moet u bepalen of u verwacht dat de verwijzing niet nul is, inspecteer de variabele en controleer of deze naar een exemplaar verwijst wanneer u dat verwacht.

Door de programmastroom op deze manier te volgen, kunt u de locatie vinden waar de instantie niet nul moet zijn en waarom deze niet correct is ingesteld.

Voorbeelden

Enkele veel voorkomende scenario's waarbij de uitzondering kan worden gegenereerd:

Algemeen

ref1.ref2.ref3.member

Als ref1 of ref2 of ref3 nul is, krijg je een NullReferenceException. Als je het probleem wilt oplossen, zoek dan uit welke null is door de uitdrukking te herschrijven naar het eenvoudiger equivalent:

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

In het bijzonder, in HttpContext.Current.User.Identity.Name, de HttpContext.Current kan nul zijn, of de User eigenschap kan nul zijn, of de Identity eigendom kan nul zijn.

indirect

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

Als u de null-verwijzing child (persoon) wilt vermijden, kunt u deze initialiseren in de constructor van het bovenliggende object (boek).

Initializers voor genest objecten

Hetzelfde geldt voor geneste object-initializers:

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

Dit vertaalt zich naar

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

Terwijl de new sleutelwoord wordt gebruikt, het maakt alleen een nieuw exemplaar van Book, maar geen nieuw exemplaar van Person, dus de Author het eigendom is nog steeds null.

Initializers voor geneste verzameling

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

De geneste initializers van de verzameling gedragen zich hetzelfde:

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

Dit vertaalt zich naar

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

De new Person maakt alleen een instantie van Person, maar de Books verzameling is nog steeds null. De syntaxis van de verzameling-initializer maakt geen verzameling voor p1.Books, het vertaalt alleen naar de p1.Books.Add(...) statements.

reeks

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

Array Elements

Person[] people = new Person[5];
people[0].Age = 20 // people[0] is null. The array was allocated but not
                   // initialized. There is no Person to set the Age for.

Jagged Arrays

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

Collectie / Lijst / Dictionary

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

Bereikvariabele (indirect / uitgesteld)

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.

Evenementen

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:

Als u velden anders hebt benoemd dan locals, heeft u zich misschien gerealiseerd dat u het veld nooit hebt geïnitialiseerd.

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

Dit kan worden opgelost door de conventie voor prefix-velden te volgen met een onderstrepingsteken:

private Customer _customer;

ASP.NET-pagina Levenscyclus:

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

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

ASP.NET MVC lege weergavemodellen

Als de uitzondering optreedt bij het verwijzen naar een eigenschap van @Model in een ASP.NET MVC-weergave moet je begrijpen dat de Model wordt ingesteld in uw actie methode, wanneer u return uitzicht. Wanneer u een leeg model (of modeleigenschap) van uw controller retourneert, treedt de uitzondering op wanneer de weergaven er toegang toe hebben:

// 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-bestellingsopdracht en -evenementen

WPF-besturingselementen worden gemaakt tijdens de oproep naar InitializeComponent in de volgorde waarin ze in de visuele structuur verschijnen. EEN NullReferenceException zal worden verhoogd in het geval van vroeg aangemaakte controles met event handlers, enz., die tijdens het vuur vuren InitializeComponent welke verwijzen naar laat-aangemaakte besturingselementen.

Bijvoorbeeld :

<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 is eerder gemaakt label1. Als comboBox1_SelectionChanged probeert te verwijzen naar `label1, het is nog niet gemaakt.

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

De volgorde van de declaraties in de XAML wijzigen (d.w.z. lijst label1 voor comboBox1, het negeren van problemen van ontwerpfilosofie, zou op zijn minst het oplossen van NullReferenceException hier.

Cast met as

var myThing = someObject as Thing;

Dit werpt geen InvalidCastException maar geeft a terug null wanneer de cast mislukt (en wanneer someObject zelf null is). Wees je daar dus van bewust.

LINQ FirstOrDefault () en SingleOrDefault ()

De gewone versies First() en Single() werp uitzonderingen wanneer er niets is. De versies "OrDefault" worden in dat geval null geretourneerd. Wees je daar dus van bewust.

foreach

foreach gooit wanneer u de null-verzameling probeert te herhalen. Meestal veroorzaakt door onverwacht null resultaat van methoden die collecties retourneren.

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

Meer realistisch voorbeeld - selecteer knooppunten uit XML-document. Wordt gegooid als er geen knooppunten worden gevonden, maar bij het eerste foutopsporing is gebleken dat alle eigenschappen geldig zijn:

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

Manieren om te vermijden

Controleer expliciet op null en negeer waarden.

Als u verwacht dat de verwijzing soms nul is, kunt u controleren of dit zo is null voor de toegang tot instanties van instanties:

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

Controleer expliciet op null en geef een standaardwaarde op.

Methoden bellen die u verwacht te retourneren als een instantie kan terugkeren null, bijvoorbeeld wanneer het gezochte object niet kan worden gevonden. U kunt ervoor kiezen om een ​​standaardwaarde te retourneren wanneer dit het geval is:

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

Controleer expliciet op null van methode-aanroepen en gooi een aangepaste uitzondering.

Je kunt ook een aangepaste uitzondering maken, alleen om deze in de aanroepcode te vangen:

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

Gebruik Debug.Assert als een waarde nooit zou moeten zijn null, om het probleem eerder op te vangen dan dat de uitzondering zich voordoet.

Wanneer je tijdens de ontwikkeling weet dat een methode misschien wel kan, maar nooit zou moeten terugkeren null, je kunt gebruiken Debug.Assert() zo snel mogelijk breken wanneer het zich voordoet:

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

Hoewel deze controle zal niet eindigen in je versie-build, waardoor het de NullReferenceException opnieuw wanneer book == null tijdens runtime in de releasemodus.

Gebruik GetValueOrDefault() voor nullable waardetypes om een ​​standaardwaarde te geven wanneer ze dat zijn 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

Gebruik de null coalescing-operator: ?? [C #] of If() [VB].

De afkorting om een ​​standaardwaarde te geven wanneer a null wordt aangetroffen:

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

Gebruik de nulvoorwaarde-operator: ?. of ?[x] voor arrays (beschikbaar in C # 6 en VB.NET 14):

Dit wordt ook wel de operator voor veilige navigatie of Elvis (naar zijn vorm) genoemd. Als de uitdrukking aan de linkerkant van de operator null is, wordt de rechterzijde niet geëvalueerd en wordt null als resultaat gegeven. Dat betekent gevallen als deze:

var title = person.Title.ToUpper();

Als de persoon geen titel heeft, geeft dit een uitzondering omdat deze probeert te bellen ToUpper op een eigenschap met een nulwaarde.

In C # 5 en lager kan dit worden bewaakt met:

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

Nu is de titelvariabele null in plaats van een uitzondering te genereren. C # 6 introduceert hiervoor een kortere syntaxis:

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

Dit zal resulteren in de titelvariabele nullen de oproep aan ToUpper is niet gemaakt als person.Title is null.

Jij natuurlijk nog steeds moet controleren title voor nul of gebruik de operator voor de nulomstandigheid samen met de operator voor de nulcoalescentie (??) om een ​​standaardwaarde te leveren:

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

Evenzo kunt u arrays gebruiken ?[i] als volgt:

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

Dit doet het volgende: Als myIntArray null is, wordt de expressie null geretourneerd en kunt u deze veilig controleren. Als het een array bevat, doet het hetzelfde als: elem = myIntArray[i]; en retourneert de ith element.

Speciale technieken voor foutopsporing en het vaststellen van nul-derefs in iteratoren

C # ondersteunt "iteratorblokken" (in sommige andere populaire talen "generators" genoemd). Null dereferentie-uitzonderingen kunnen bijzonder lastig zijn om te debuggen in iteratorblokken vanwege uitgestelde uitvoering:

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

Als whatever resulteert in null dan MakeFrob zal gooien. Nu, je zou kunnen denken dat het juiste ding om te doen is dit:

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

Waarom is dit verkeerd? Omdat het iteratorblok eigenlijk niet rennen tot de foreach! De oproep aan GetFrobs geeft eenvoudig een voorwerp terug dat wanneer geïtereerd zal het iteratorblok uitvoeren.

Door een nulcontrole als deze te schrijven voorkom je de nul dereferentie, maar verplaats je de nul-argumentuitzondering naar het punt van de herhaling, niet tot het punt van de telefoontje, en dat is erg verwarrend om te debuggen.

De juiste oplossing is:

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

Dat wil zeggen, maak een privéhulpmethode met de iteratorbloklogica en een openbare oppervlakmethode die de nulcontrole uitvoert en de iterator retourneert. Nu wanneer GetFrobs wordt aangeroepen, gebeurt de nulcontrole onmiddellijk en vervolgens GetFrobsForReal wordt uitgevoerd wanneer de reeks wordt herhaald.

Als u de referentiebron voor LINQ naar objecten bekijkt, ziet u dat deze techniek overal wordt gebruikt. Het is iets meer onhandig om te schrijven, maar het maakt het debuggen van nietigheidsfouten veel gemakkelijker. Optimaliseer uw code voor het gemak van de beller, niet het gemak van de auteur.

Een opmerking over null-dereferenties in onveilige code

C # heeft een "onveilige" modus die, zoals de naam al aangeeft, buitengewoon gevaarlijk is omdat de normale veiligheidsmechanismen die zorgen voor geheugenveiligheid en veiligheid van het type niet worden afgedwongen. U mag geen onveilige code schrijven tenzij u een grondig en diep begrip hebt van hoe het geheugen werkt.

In de onveilige modus moet u rekening houden met twee belangrijke feiten:

  • dereferencing a null wijzer produceert dezelfde uitzondering als het verwijderen van een null referentie
  • dereferencing van een ongeldige niet-lege aanwijzer kan produceer die uitzondering in sommige omstandigheden

Om te begrijpen waarom dat zo is, helpt het om te begrijpen hoe .NET null dereferentie-uitzonderingen produceert in de eerste plaats. (Deze details zijn van toepassing op .NET op Windows, andere besturingssystemen gebruiken vergelijkbare mechanismen.)

Geheugen is gevirtualiseerd in Windows; elk proces krijgt een virtueel geheugenruimte van vele "pagina's" van het geheugen die worden bijgehouden door het besturingssysteem. Elke pagina van het geheugen bevat vlaggen die bepalen hoe deze kan worden gebruikt: lezen van, schrijven naar, uitvoeren, enzovoort. De laagste pagina is gemarkeerd als "produceer een fout als deze ooit wordt gebruikt".

Zowel een nulaanwijzer als een nulreferentie in C # worden intern weergegeven als het getal nul en elke poging om het in de bijbehorende geheugenopslag te de-leiden, zorgt ervoor dat het besturingssysteem een ​​fout produceert. De runtime van .NET detecteert deze fout vervolgens in de nulderheidsuitzondering.

Dat is de reden waarom het afleiden van zowel een nulaanwijzer als een nulreferentie dezelfde uitzondering produceert.

Hoe zit het met het tweede punt? dereferentie ieder een ongeldige pointer die op de laagste pagina van het virtuele geheugen valt, veroorzaakt dezelfde fout van het besturingssysteem en daarmee dezelfde uitzondering.

Waarom is dit logisch? Nou, stel dat we een struct hebben met twee ints en een niet-beheerde aanwijzer gelijk aan nul. Als we proberen de tweede int in de struct te de-refereren, zal de CLR niet proberen toegang te krijgen tot de opslag op locatie nul; het zal toegang krijgen tot de opslag op locatie vier. Maar logisch gezien is dit een nulverwijzing omdat we naar dat adres gaan via de nul.

Als u met onveilige code werkt en u krijgt een nulderheidsuitzondering, moet u er rekening mee houden dat de aanstellerige aanwijzer niet nul hoeft te zijn. Dit kan elke locatie op de laagste pagina zijn en deze uitzondering wordt geproduceerd.


2107



NullReference Uitzondering - Visual Basic

De NullReference Exception voor Visual Basic is niet anders dan die in C #. Ze rapporteren immers allebei dezelfde uitzondering die is gedefinieerd in het .NET Framework dat ze beide gebruiken. Oorzaken die uniek zijn voor Visual Basic zijn zeldzaam (misschien slechts één).

Dit antwoord gebruikt Visual Basic-termen, syntaxis en context. De gebruikte voorbeelden zijn afkomstig van een groot aantal eerdere Stack Overflow-vragen. Dit is om de relevantie te maximaliseren met behulp van de soorten van situaties die vaak worden gezien in berichten. Een beetje meer uitleg is ook voorzien voor diegenen die het misschien nodig hebben. Een vergelijkbaar voorbeeld als het uwe is heel waarschijnlijk hier vermeld.

Notitie:

  1. Dit is conceptgebaseerd: er is geen code die u in uw project kunt plakken. Het is bedoeld om u te helpen begrijpen wat de oorzaak is van een NullReferenceException (NRE), hoe u het kunt vinden, hoe u het kunt repareren en hoe u het kunt vermijden. Een NRE kan op veel manieren worden veroorzaakt, dus dit is waarschijnlijk niet uw enige ontmoeting.
  2. De voorbeelden (van Stack Overflow-berichten) laten niet altijd de beste manier zien om iets te doen in de eerste plaats.
  3. Meestal wordt de eenvoudigste remedie gebruikt.

Basisbetekenis

De boodschap "Object niet ingesteld op een exemplaar van Object" betekent dat u een object probeert te gebruiken dat niet is geïnitialiseerd. Dit komt neer op een van deze:

  • Jouw code verklaard een objectvariabele, maar dat gebeurde niet initialiseren het (maak een instantie of 'instantiëren'het)
  • Iets dat volgens uw code een object zou initialiseren, deed dat niet
  • Mogelijk heeft andere code een object dat nog in gebruik is, vroegtijdig ongeldig gemaakt

De oorzaak vinden

Omdat het probleem een ​​objectreferentie is Nothing, het antwoord is om ze te onderzoeken om erachter te komen welke. Bepaal vervolgens waarom het niet is geïnitialiseerd. Houd de muis boven de verschillende variabelen en Visual Studio (VS) toont hun waarden - de boosdoener zal zijn Nothing.

IDE debug display

Je moet ook alle Try / Catch-blokken uit de relevante code verwijderen, vooral degene die niets bevatten in het catch-blok. Dit zorgt ervoor dat uw code crasht wanneer deze probeert een object te gebruiken dat dat wel is Nothing. Dit is wat je wilt omdat het exact zal identificeren plaats van het probleem, en u in staat stellen om het object dat dit veroorzaakt te identificeren.

EEN MsgBox in de Catch die wordt weergegeven Error while... zal van weinig hulp zijn. Deze methode leidt ook tot heel slecht Stack Overflow-vragen, omdat u de daadwerkelijke uitzondering, het betreffende object of zelfs de regel code niet kunt beschrijven waar deze zich voordoet.

U kunt ook de Locals Window (Debug -> Windows -> Locals) om je objecten te bekijken.

Zodra u weet wat en waar het probleem is, is het meestal vrij eenvoudig op te lossen en sneller dan het plaatsen van een nieuwe vraag.

Zie ook:

Voorbeelden en oplossingen

Klasse-objecten / Een exemplaar maken

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

Het probleem is dat Dim maakt geen CashRegister aan voorwerp; het verklaart alleen een variabele met de naam reg van dat type. Verklaren een objectvariabele en een maken aanleg zijn twee verschillende dingen.

Remedie

De New operator kan vaak worden gebruikt om het exemplaar te maken wanneer u het declareert:

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

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

Wanneer het alleen passend is om de instantie later te maken:

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

Notitie: Niet doen gebruik Dim opnieuw in een procedure, inclusief de constructor (Sub New):

Private reg As CashRegister
'...

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

Dit zal een maken lokaal variabel, reg, die alleen in die context bestaat (sub). De reg variabele met moduleniveau Scope die je overal elders zult gebruiken, blijft Nothing.

Het missen van de New operator is de # 1 oorzaak van NullReference Exceptions gezien in de Stack Overflow-vragen beoordeeld.

Visual Basic probeert het proces herhaaldelijk te wissen met behulp van New: De ... gebruiken New Operator maakt een nieuwe object en oproepen Sub New - de constructor - waar uw object elke andere initialisatie kan uitvoeren.

Om duidelijk te zijn, Dim (of Private) enkel en alleen verklaart een variabele en zijn Type. De strekking van de variabele - of deze nu voor de hele module / klasse bestaat of voor een procedure lokaal is - wordt bepaald door waar het is verklaard. Private | Friend | Public definieert het toegangsniveau, niet strekking.

Zie voor meer informatie:


arrays

Arrays moeten ook worden geïnstantieerd:

Private arr as String()

Deze array is alleen gedeclareerd, niet gemaakt. Er zijn verschillende manieren om een ​​array te initialiseren:

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

Opmerking: te beginnen met VS 2010, bij het initialiseren van een lokale array met behulp van een letterlijke en Option Infer, de As <Type> en New elementen zijn optioneel:

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}

Het gegevenstype en de matrixgrootte worden afgeleid van de gegevens die worden toegewezen. Klasse- / module-niveauverklaringen zijn nog steeds vereist As <Type> met Option Strict:

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

Voorbeeld: array van klasseobjecten

Dim arrFoo(5) As Foo

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

De array is gemaakt, maar de Fooobjecten erin hebben dat niet.

Remedie

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

Gebruik maken van een List(Of T) zal het heel moeilijk maken om een ​​element zonder geldig object te hebben:

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

Zie voor meer informatie:


Lijsten en collecties

.NET-collecties (waarvan er vele zijn: lijsten, woordenboek, enz.) Moeten ook worden geïnstantieerd of gemaakt.

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

U krijgt dezelfde uitzondering om dezelfde reden - myList werd alleen verklaard, maar er is geen instantie gemaakt. De remedie is hetzelfde:

myList = New List(Of String)

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

Een veelgehoord overzicht is een klasse die een verzameling gebruikt 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

Beide procedures resulteren in een NRE, omdat barList is alleen gedeclareerd, niet geïnstantieerd. Een instantie maken van Foo zal ook geen instantie van de interne aanmaken barList. Het kan de bedoeling zijn geweest om dit in de constructor te doen:

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

Zoals voorheen is dit onjuist:

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

Zie voor meer informatie List(Of T) Klasse.


Gegevensproviderobjecten

Werken met databases biedt veel mogelijkheden voor een NullReference omdat er veel objecten kunnen zijn (Command, Connection, Transaction, Dataset, DataTable, DataRows....) tegelijk in gebruik. Notitie: Het maakt niet uit welke gegevensprovider u gebruikt - MySQL, SQL Server, OleDB, etc. - de concepten zijn hetzelfde.

voorbeeld 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

Zoals eerder, de ds Dataset-object is gedeclareerd, maar er is nooit een exemplaar gemaakt. De DataAdapter zal een bestaand vullen DataSet, maak er geen. In dit geval, sinds ds is een lokale variabele, de IDE waarschuwt je dat dit kan gebeuren:

img

Indien gedeclareerd als een module / klasse niveau variabele, zoals het geval lijkt te zijn met con, de compiler kan niet weten of het object is gemaakt door een stroomopwaartse procedure. Negeer waarschuwingen niet.

Remedie

Dim ds As New DataSet

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

Een typfout is hier een probleem: Employees vs Employee. Er was geen DataTable genaamd "Werknemer" gemaakt, dus a NullReferenceException resultaten proberen toegang te krijgen. Een ander potentieel probleem is ervan uitgaande dat er zal zijn Items dat is misschien niet het geval wanneer de SQL een WHERE-component bevat.

Remedie

Omdat dit één tabel gebruikt, gebruikt Tables(0) voorkomt spelfouten. Onderzoeken Rows.Count kan ook helpen:

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 is een functie die het aantal retourneert Rows getroffen die ook kan worden getest:

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

Voorbeeld 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

De DataAdapter zal bieden TableNames zoals getoond in het vorige voorbeeld, maar het ontleedt geen namen uit de SQL- of databasetabel. Als gevolg, ds.Tables("TICKET_RESERVATION") verwijst naar een niet-bestaande tabel.

De Remedie is hetzelfde, verwijs de tabel door index:

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

Zie ook DataTable-klasse.


Objectpaden / genest

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

De code is alleen testen Items terwijl beide myFoo en Bar kan ook niets zijn. De remedie is om de hele keten of het hele pad van objecten één voor één te testen:

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

AndAlso is belangrijk. Daaropvolgende tests worden niet de eerste keer uitgevoerd False voorwaarde is aangetroffen. Hierdoor kan de code 'één niveau' veilig in het object (de objecten) 'boren' en evalueren myFoo.Bar alleen na (en als) myFoo is vastgesteld geldig te zijn. Objectketens of paden kunnen behoorlijk lang worden bij het coderen van complexe objecten:

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

Het is niet mogelijk om naar iets 'stroomafwaarts' van a te verwijzen null voorwerp. Dit geldt ook voor bedieningselementen:

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

Hier, myWebBrowser of Document kan Niets of de zijn formfld1 element bestaat mogelijk niet.


UI-bedieningselementen

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)

Deze code voorziet onder andere niet dat de gebruiker mogelijk niet iets heeft geselecteerd in een of meer UI-besturingselementen. ListBox1.SelectedItem het zou kunnen zijn Nothing, dus ListBox1.SelectedItem.ToString zal resulteren in een NRE.

Remedie

Valideer gegevens voordat u het gebruikt (gebruik ook Option Strict en SQL-parameters):

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

Als alternatief kunt u gebruiken (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Visual Basic Forms

Public Class Form1

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

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

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

Dit is een vrij gebruikelijke manier om een ​​NRE te krijgen. In C # rapporteert de IDE, afhankelijk van hoe het is gecodeerd Controls bestaat niet in de huidige context, of "kan niet verwijzen naar niet-statisch lid". Dus tot op zekere hoogte is dit een VB-only situatie. Het is ook complex omdat het kan resulteren in een mislukte cascade.

De arrays en verzamelingen kunnen niet op deze manier worden geïnitialiseerd. Deze initialisatiecode wordt uitgevoerd voor de constructor maakt de Form of de Controls. Als gevolg:

  • Lijsten en verzameling zijn gewoon leeg
  • De array bevat vijf elementen van Nothing
  • De somevar toewijzing resulteert in een onmiddellijke NRE omdat Niets geen a heeft .Text eigendom

Als u later verwijst naar arrayelementen, resulteert dit in een NRE. Als je dit doet Form_Load, vanwege een vreemde bug, de IDE mag niet meld de uitzondering wanneer dit gebeurt. De uitzondering verschijnt later wanneer uw code de array probeert te gebruiken. Deze "stille uitzondering" is gedetailleerd in dit bericht. Voor onze doeleinden is de sleutel dat wanneer er iets catastrofaals gebeurt tijdens het maken van een formulier (Sub New of Form Load evenement), uitzonderingen kunnen niet worden gerapporteerd, de code verlaat de procedure en geeft gewoon het formulier weer.

Aangezien er geen andere code in uw Sub New of Form Load evenement zal lopen na de NRE, heel veel andere dingen kan niet-geïnitialiseerd worden gelaten.

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

Notitie dit is van toepassing op alle besturingselementen en componentreferenties waardoor deze illegaal worden gemaakt waar ze zijn:

Public Class Form1

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

Gedeeltelijke remedie

Het is merkwaardig dat VB geen waarschuwing geeft, maar de remedie is om verklaren de containers op form niveau, maar initialiseren ze in formulier load event handler wanneer de besturing do bestaan. Dit kan gedaan worden in Sub New zo lang als uw code achter de InitializeComponent bellen:

' 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

De arraycode is misschien nog niet uit het bos. Alle besturingselementen die zich in een containerbesturingselement bevinden (zoals een GroupBox of Panel) zal niet worden gevonden in Me.Controls; ze staan ​​in de Controls-verzameling van dat paneel of die GroupBox. Noch zal een controle worden geretourneerd wanneer de controlenaam verkeerd gespeld is ("TeStBox2"). In dergelijke gevallen, Nothing wordt opnieuw opgeslagen in die array-elementen en er zal een NRE optreden wanneer u ernaar probeert te verwijzen.

Deze moeten gemakkelijk te vinden zijn nu je weet waar je naar op zoek bent: VS shows you the error of your ways

"Button2" bevindt zich op een Panel

Remedie

In plaats van indirecte verwijzingen op naam met behulp van de formulieren Controlsverzameling, gebruik de controlereferentie:

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

Functie Niets terugkrijgen

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

Dit is een geval waarin de IDE u zal waarschuwen dat 'niet alle paden retourneren een waarde en een NullReferenceException kan resulteren'. U kunt de waarschuwing onderdrukken door te vervangen Exit Function met Return Nothing, maar dat lost het probleem niet op. Alles dat de return probeert te gebruiken wanneer someCondition = False zal resulteren in een NRE:

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

Remedie

Vervangen Exit Function in de functie met Return bList. Terugkeren leeg  List is niet hetzelfde als terugkeren Nothing. Als er een kans is dat een geretourneerd object kan zijn Nothing, test voor gebruik:

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

Slecht geïmplementeerde Try / Catch

Een slecht geïmplementeerde Try / Catch kan verbergen waar het probleem zit en resulteren in nieuwe:

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

Dit is een geval waarbij een object niet wordt gemaakt zoals verwacht, maar ook het tegengewicht van een leeg object aantoont Catch.

Er is een extra komma in de SQL (na 'mailaddress') die resulteert in een uitzondering op .ExecuteReader. Na de Catch doet niets, Finally probeert op te ruimen, maar aangezien je dat niet kunt Close een nul DataReader object, een gloednieuw object NullReferenceException resultaten.

Een lege Catch blok is de speelplaats van de duivel. Dit OP was verbijsterd waarom hij een NRE kreeg in de Finally blok. In andere situaties is een leeg Catch kan ertoe leiden dat iets verderop in de war raakt en zorgt ervoor dat u tijd besteedt aan het kijken naar de verkeerde dingen op de verkeerde plaats voor het probleem. (De "stille uitzondering" hierboven beschreven biedt dezelfde entertainmentwaarde.)

Remedie

Gebruik geen lege Try / Catch-blokken - laat de code crashen zodat u a) de oorzaak kunt identificeren b) de locatie kunt identificeren en c) een goede oplossing kunt toepassen. Try / Catch-blokken zijn niet bedoeld om uitzonderingen te verbergen voor de persoon die speciaal is gekwalificeerd om ze te repareren - de ontwikkelaar.


DBNull is niet hetzelfde als niets

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

De IsDBNull functie wordt gebruikt om te testen of a waarde is gelijk aan System.DBNull: Van MSDN:

De waarde System.DBNull geeft aan dat het object ontbrekende of niet-bestaande gegevens vertegenwoordigt. DBNull is niet hetzelfde als Nothing, wat aangeeft dat een variabele nog niet is geïnitialiseerd.

Remedie

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

Net als eerder kunt u testen op Niets dan op een specifieke waarde:

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

Voorbeeld 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 retourneert het eerste item of de standaardwaarde, wat is Nothing voor referentietypes en nooit DBNull:

If getFoo IsNot Nothing Then...

controls

Dim chk As CheckBox

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

Als een CheckBox met chkName kan niet worden gevonden (of bestaat in een GroupBox), dan chk zal Niets zijn en proberen om naar enige eigenschap te verwijzen zal resulteren in een uitzondering.

Remedie

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

De DataGridView

De DGV heeft regelmatig wat eigenaardigheden:

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"

Als dgvBooks heeft AutoGenerateColumns = True, het zal de kolommen maken, maar het noemt ze niet, dus de bovenstaande code faalt wanneer het naar hen verwijst op naam.

Remedie

Noem de kolommen handmatig, of verwijs naar index:

dgvBooks.Columns(0).Visible = True

Voorbeeld 2 - Pas op voor de 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

Wanneer je DataGridView heeft AllowUserToAddRows als True (de standaard), de Cells in de lege / nieuwe rij onderaan zullen alle bevatten Nothing. De meeste pogingen om de inhoud te gebruiken (bijvoorbeeld ToString) resulteert in een NRE.

Remedie

Gebruik een For/Each loop en test de IsNewRow eigenschap om te bepalen of het die laatste rij is. Dit werkt of AllowUserToAddRows is waar of niet:

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

Als u een a gebruikt For n loop, wijzig het aantal rijen of gebruik Exit For wanneer IsNewRow is waar.


My.Settings (StringCollection)

Onder bepaalde omstandigheden probeert u een item te gebruiken My.Settings wat een is StringCollection kan resulteren in een NullReference de eerste keer dat u het gebruikt. De oplossing is hetzelfde, maar niet zo voor de hand liggend. Overwegen:

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

Omdat VB Instellingen voor u beheert, is het redelijk om te verwachten dat het de verzameling initialiseert. Dit gebeurt, maar alleen als u eerder een eerste vermelding aan de verzameling hebt toegevoegd (in de Editor voor instellingen). Omdat de verzameling (kennelijk) wordt geïnitialiseerd wanneer een item wordt toegevoegd, blijft het Nothing wanneer er geen items in de instellingeneditor zijn om toe te voegen.

Remedie

Initialiseer de instellingenverzameling in de formulieren Load gebeurtenishandler, indien / wanneer nodig:

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

Typisch, de Settings verzameling hoeft alleen te worden geïnitialiseerd wanneer de toepassing voor de eerste keer wordt uitgevoerd. Een alternatieve oplossing is om een ​​beginwaarde toe te voegen aan uw verzameling in Project -> Instellingen | FooBars, sla het project op en verwijder de valse waarde.


Sleutelpunten

Je hebt de. Waarschijnlijk vergeten New operator.

of

Iets waarvan u aannam dat het foutloos zou werken om een ​​geïnitialiseerd object terug te sturen naar uw code, niet.

Negeer nooit compilerwaarschuwingen (ooit) en gebruik Option Strict On (altijd).


MSDN NullReference-uitzondering


273



Een ander scenario is wanneer u een null-object in een cast waarde type. Bijvoorbeeld de onderstaande code:

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

Het zal gooien een NullReferenceException op de cast. Het lijkt vrij duidelijk in het bovenstaande voorbeeld, maar dit kan gebeuren in meer "laat-bindende" ingewikkelde scenario's waarbij het nul-object is geretourneerd van een code die je niet bezit, en de cast wordt bijvoorbeeld gegenereerd door een of ander automatisch systeem.

Een voorbeeld hiervan is dit eenvoudige ASP.NET-bindingsfragment met het kalenderbesturingselement:

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

Hier, SelectedDate is in feite een eigenschap - van DateTime type - van de Calendar Web Control type, en de binding zou perfect null kunnen retourneren. De impliciete ASP.NET-generator maakt een stuk code dat gelijkwaardig is aan de bovenstaande castcode. En dit zal een verhogen NullReferenceException dat is vrij moeilijk te herkennen, omdat het ligt in ASP.NET gegenereerde code die prima compileert ...


217



Het betekent dat de variabele in kwestie op niets gericht is. Ik zou dit als volgt kunnen genereren:

SqlConnection connection = null;
connection.Open();

Dat zal de fout veroorzaken omdat ik de variabele heb verklaard "connection", er wordt nergens op gewezen, wanneer ik het lid probeer te bellen"Open", er is geen referentie om het op te lossen, en het zal de fout veroorzaken.

Om deze fout te voorkomen:

  1. Initialiseer altijd uw objecten voordat u iets met ze probeert te doen.
  2. Als u niet zeker weet of het object null is, controleert u dit met object == null.

De Resharper-tool van JetBrains identificeert elke plaats in uw code die de mogelijkheid heeft van een nulreferentiefout, waardoor u een nulcontrole kunt uitvoeren. Deze fout is de nummer één bron van bugs, IMHO.


146



Dit betekent dat uw code een objectverwijzingsvariabele heeft gebruikt die op nul is ingesteld (d.w.z. dat deze niet naar een werkelijke objectinstantie verwijst).

Om de fout te voorkomen, moeten objecten die nul zouden kunnen zijn, worden getest op nul voordat ze worden gebruikt.

if (myvar != null)
{
    // Go ahead and use myvar
    myvar.property = ...
}
else
{
    // Whoops! myvar is null and cannot be used without first
    // assigning it to an instance reference
    // Attempting to use myvar here will result in NullReferenceException
}

135



Houd er rekening mee dat, ongeacht het scenario, de oorzaak altijd dezelfde is in .NET:

U probeert een referentievariabele te gebruiken waarvan de waarde is Nothing/null. Wanneer de waarde is Nothing/null voor de referentievariabele betekent dit dat er feitelijk geen verwijzing is naar een exemplaar van een object dat op de heap bestaat.

U hebt nooit iets aan de variabele toegewezen, hebt nooit een instantie van de waarde die aan de variabele is toegewezen, of u stelt de variabele gelijk aan in Nothing/null handmatig of u hebt een functie aangeroepen waarmee de variabele is ingesteld Nothing/null voor jou.


90



Een voorbeeld van deze uitzondering is: als u iets probeert te controleren, is dat null.

Bijvoorbeeld:

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
} 

De .NET-runtime genereert een NullReferenceException wanneer u probeert een actie uit te voeren op iets dat niet is geïnstantieerd, dat wil zeggen de bovenstaande code.

In vergelijking met een ArgumentNullException die typisch als een verdedigende maatregel wordt gegooid als een methode verwacht dat wat wordt doorgegeven niet nul is.

Meer informatie is beschikbaar C # NullReferenceException en Null Parameter.


76



Als u een referentietype niet heeft geïnitialiseerd en u een van zijn eigenschappen wilt instellen of lezen, wordt er een NullReferenceException.

Voorbeeld:

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

U kunt dit gewoon vermijden door te controleren of de variabele null is:

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

Om volledig te begrijpen waarom een ​​NullReferenceException wordt gegenereerd, is het belangrijk om het verschil tussen te kennen waardetypes en referentietypen.

Dus, als je te maken hebt met waardetypes, NullReferenceExceptions kan niet optreden. Hoewel je alert moet blijven op het omgaan met referentietypen!

Alleen referentietypen, zoals de naam suggereert, kunnen referenties bevatten of letterlijk naar niks verwijzen (of 'null'). Waarden van waarden bevatten altijd een waarde.

Referentietypen (deze moeten worden gecontroleerd):

  • dynamisch
  • voorwerp
  • draad

Waardetypes (u kunt deze eenvoudigweg negeren):

  • Numerieke typen
  • Integrale typen
  • Floating-point typen
  • decimale
  • bool
  • Door de gebruiker gedefinieerde structs

72



Een ander geval waar NullReferenceExceptions kan gebeuren is het (onjuiste) gebruik van de as operator:

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

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

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

Hier, Book en Car zijn incompatibele typen; een Car kan niet worden geconverteerd / cast naar een Book. Wanneer deze cast mislukt, as komt terug null. Gebruik makend van mybook na dit veroorzaakt een NullReferenceException.

Over het algemeen moet u een cast of gebruiken as, als volgt:

Als u verwacht dat de typeconversie altijd zal slagen (dat wil zeggen u weet wat het object van tevoren moet zijn), dan zou u een cast moeten gebruiken:

ComicBook cb = (ComicBook)specificBook;

Als je niet zeker bent van het type, maar je wilt proberen om het als een specifiek type te gebruiken, gebruik dan as:

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

65



U gebruikt het object dat de nulwaarde-verwijzing bevat. Dus het geeft een nuluitzondering. In het voorbeeld is de tekenreekswaarde null en bij het controleren van de lengte ervan, trad de uitzondering op.

Voorbeeld:

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

De uitzonderingsfout is:

Onbehandelde uitzondering:

System.NullReferenceException: Objectreferentie is niet ingesteld op een instantie   van een object. bij Program.Main ()


59



Simon Mourier gaf dit voorbeeld:

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

waar een unboxing conversie (cast) van  object (of van een van de klassen System.ValueType of System.Enumof van een interfacetype) naar een waardetype (anders dan Nullable<>) geeft op zichzelf het NullReferenceException.

In de andere richting, a boksen conversie van een Nullable<> welke heeft HasValue gelijk aan false  naar een referentietype, kan een null verwijzing die later kan leiden tot een NullReferenceException. Het klassieke voorbeeld is:

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

Soms gebeurt het boksen op een andere manier. Bijvoorbeeld met deze niet-generieke uitbreidingsmethode:

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

de volgende code zal problematisch zijn:

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

Deze gevallen ontstaan ​​vanwege de speciale regels die de runtime gebruikt bij het boksen Nullable<> instances.


48