Che cos'è un NullReferenceException e come posso risolverlo?


1875

Ho un po 'di codice e quando viene eseguito, genera un NullReferenceException, dicendo:

Il riferimento non impostato su un'istanza di un oggetto.

Che cosa significa questo e cosa posso fare per correggere questo errore?


L'helper delle eccezioni in VS 2017 sarà più utile nella diagnosi della causa di questa eccezione: blogs.msdn.microsoft.com/visualstudio/2016/11/28/… in New Exception Helper .
Zev Spitz,

Cari futuri visitatori, le risposte a questa domanda si applicano ugualmente a ArgumentNullException . Se la tua domanda è stata chiusa come duplicata di questa e stai riscontrando un ANE, segui le istruzioni nelle risposte per eseguire il debug e risolvere il problema.

@will ANE dovrebbe accadere solo se viene passato un null come parametro. Puoi fare un esempio se una domanda ANE viene chiusa come duplicata di questa?
John Saunders,

È venuto su Meta, ma dovrei andare a cercare il link. Ma per quanto riguarda quel commento, un ANE è semplicemente un NRE ma qualcuno ha aggiunto un controllo preventivo e almeno sai esattamente cosa è null (viene fornito il nome dell'argomento), quindi è un po 'più facile da diagnosticare di un NRE semplice.

Risposte:


2416

Qual è la causa?

Linea di fondo

Stai tentando di utilizzare qualcosa che è null(o Nothingin VB.NET). Questo significa che l'hai impostato su nullo non l'hai mai impostato su niente.

Come ogni altra cosa, nullviene passato in giro. Se è null in modalità "A", potrebbe essere che il metodo "B" ha approvato una null al metodo di "A".

null può avere significati diversi:

    1. Variabili oggetto che non sono inizializzate e quindi non indicano nulla. In questo caso, se si accede a proprietà o metodi di tali oggetti, provoca a NullReferenceException.
    1. Lo sviluppatore utilizza nullintenzionalmente per indicare che non è disponibile alcun valore significativo. Si noti che C # ha il concetto di tipi di dati nullable per le variabili (come le tabelle del database possono avere campi nullable) - è possibile assegnare nullloro per indicare che non vi è alcun valore memorizzato in esso, ad esempio int? a = null;dove il punto interrogativo indica che è consentito archiviare null in variabile a. Puoi verificarlo con if (a.HasValue) {...}o con if (a==null) {...}. Le variabili nullable, come in aquesto esempio, consentono di accedere al valore in modo a.Valueesplicito o altrettanto normale tramite a.
      Nota che l'accesso tramite a.Valuegenera InvalidOperationExceptioninvece di una NullReferenceException, se anon è innull- dovresti fare il controllo in anticipo, cioè se hai un'altra variabile annullabile, int b;allora dovresti fare incarichi simili if (a.HasValue) { b = a.Value; }o più brevi if (a != null) { b = a; }.

Il resto di questo articolo approfondisce e mostra gli errori che molti programmatori fanno spesso che possono portare a NullReferenceException.

Più specificamente

Il runtimegettando un NullReferenceException sempre significa la stessa cosa: si sta tentando di usare un riferimento, e il riferimento non è inizializzata (o era una volta inizializzato, ma è non è più inizializzato).

Ciò significa che il riferimento è nulle non è possibile accedere ai membri (come i metodi) tramite un nullriferimento. Il caso più semplice:

string foo = null;
foo.ToUpper();

Questo lancerà NullReferenceExceptiona sulla seconda riga perché non puoi chiamare il metodo di istanza ToUpper()su un stringriferimento che punta a null.

Debug

Come trovi la fonte di un NullReferenceException? Oltre a guardare l'eccezione stessa, che verrà generata esattamente nella posizione in cui si verifica, si applicano le regole generali di debug in Visual Studio: posizionare punti di interruzione strategici e ispezionare le variabili , passando il mouse sui loro nomi, aprendo un ( Veloce) Guarda la finestra o usando i vari pannelli di debug come Locali e Auto.

Se vuoi scoprire dove si trova o non è impostato il riferimento, fai clic con il pulsante destro del mouse sul nome e seleziona "Trova tutti i riferimenti". È quindi possibile posizionare un punto di interruzione in ogni posizione trovata ed eseguire il programma con il debugger collegato. Ogni volta che il debugger si interrompe su un tale punto di interruzione, è necessario determinare se si prevede che il riferimento sia non nullo, ispezionare la variabile e verificare che punti a un'istanza quando previsto.

Seguendo il flusso del programma in questo modo, è possibile trovare la posizione in cui l'istanza non deve essere nulla e perché non è impostata correttamente.

Esempi

Alcuni scenari comuni in cui è possibile generare l'eccezione:

Generico

ref1.ref2.ref3.member

Se ref1 o ref2 o ref3 sono nulli, otterrai a NullReferenceException. Se vuoi risolvere il problema, scopri quale è nullo riscrivendo l'espressione nel suo equivalente più semplice:

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

In particolare, in HttpContext.Current.User.Identity.Name, HttpContext.Currentpotrebbe essere nullo oppure la Userproprietà potrebbe essere nulla oppure la Identityproprietà potrebbe essere nulla.

indiretto

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

Se si desidera evitare il riferimento null figlio (Persona), è possibile inizializzarlo nel costruttore dell'oggetto genitore (Libro).

Inizializzatori di oggetti nidificati

Lo stesso vale per gli inizializzatori di oggetti nidificati:

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

Questo si traduce in

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

Mentre la newparola chiave viene utilizzata, crea solo una nuova istanza di Book, ma non una nuova istanza di Person, quindi Authorla proprietà è ferma null.

Inizializzatori raccolta nidificati

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

La raccolta nidificata si Initializerscomporta allo stesso modo:

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

Questo si traduce in

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

L' new Personunico crea un'istanza di Person, ma la Booksraccolta è ancora null. La Initializersintassi della raccolta non crea una raccolta per p1.Books, si traduce solo nelle p1.Books.Add(...)istruzioni.

Vettore

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

Elementi dell'array

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.

Matrici frastagliate

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.

Raccolta / Lista / Dizionario

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

Intervallo variabile (indiretto / differito)

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.

eventi

public class Demo
{
    public event EventHandler StateChanged;

    protected virtual void OnStateChanged(EventArgs e)
    {        
        StateChanged(this, e); // Exception is thrown here 
                               // if no event handlers have been attached
                               // to StateChanged event
    }
}

###Bad Naming Conventions:

If you named fields differently from locals, you might have realized that you never initialized the field. 

public class Form1 {cliente privato del cliente;

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

}

Questo può essere risolto seguendo la convenzione per aggiungere un prefisso ai campi con un trattino basso:

    private Customer _customer;

Ciclo di vita della pagina ASP.NET:

public partial class Issues_Edit : System.Web.UI.Page
{
    protected TestIssue myIssue;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
             // Only called on first load, not when button clicked
             myIssue = new TestIssue(); 
        }
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
        myIssue.Entry = "NullReferenceException here!";
    }
}

Valori di sessione ASP.NET

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

Modelli ASP.NET MVC vista vuota

Se l'eccezione si verifica quando si fa riferimento a una proprietà di @Modelin ASP.NET MVC View, è necessario comprendere che Modelviene impostato nel metodo di azione, quando si returnvisualizza. Quando si restituisce un modello vuoto (o proprietà del modello) dal controller, l'eccezione si verifica quando le viste accedono ad esso:

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

Ordine ed eventi di creazione del controllo WPF

WPFi controlli vengono creati durante la chiamata a InitializeComponentnell'ordine in cui compaiono nell'albero visivo. A NullReferenceExceptionverrà generato nel caso di controlli creati in anticipo con gestori di eventi, ecc., Quell'incendio durante il InitializeComponentquale i controlli creati in ritardo di riferimento.

Per esempio :

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

Qui comboBox1è stato creato prima label1. Se comboBox1_SelectionChangedtenta di fare riferimento a `label1, non sarà ancora stato creato.

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

Cambiare l'ordine delle dichiarazioni nel XAML(cioè, elencare label1prima comboBox1, ignorare le questioni della filosofia del design, risolverebbe almeno il NullReferenceExceptionqui.

Cast con as

var myThing = someObject as Thing;

Questo non genera un InvalidCastExceptionma restituisce un nullerrore quando il cast fallisce (e quando someObjectè nullo). Quindi sii consapevole di ciò.

LINQ FirstOrDefault()eSingleOrDefault()

Le versioni semplici First()e Single()generano eccezioni quando non c'è nulla. Le versioni "OrDefault" restituiscono null in quel caso. Quindi sii consapevole di ciò.

per ciascuno

foreachgenera quando si tenta di ripetere la raccolta null. Generalmente causato da nullrisultati imprevisti da metodi che restituiscono raccolte.

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

Esempio più realistico: selezionare i nodi dal documento XML. Verrà generato se i nodi non vengono trovati ma il debug iniziale mostra che tutte le proprietà sono valide:

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

Modi da evitare

Controllare esplicitamente nulle ignorare i valori null.

Se a volte ti aspetti che il riferimento sia nullo, puoi verificarne la presenza nullprima di accedere ai membri dell'istanza:

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

Verificare esplicitamente nulle fornire un valore predefinito.

I metodi che ti aspetti di restituire possono restituire un'istanza null, ad esempio quando non è possibile trovare l'oggetto da cercare. Puoi scegliere di restituire un valore predefinito in questo caso:

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

Controlla esplicitamente le nullchiamate di metodo e genera un'eccezione personalizzata.

Puoi anche generare un'eccezione personalizzata, solo per catturarla nel codice chiamante:

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

Utilizzare Debug.Assertse un valore non dovrebbe mai essere null, per rilevare il problema prima che si verifichi l'eccezione.

Quando sai durante lo sviluppo che un metodo può, ma non dovrebbe mai tornare null, puoi usare Debug.Assert()per interrompere il più presto possibile quando si verifica:

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

Sebbene questo controllo non finisca nella build di rilascio , causando il lancio di NullReferenceExceptionnuovo quando book == nullin fase di esecuzione in modalità di rilascio.

Utilizzare GetValueOrDefault()per i nullabletipi di valore per fornire un valore predefinito quando lo sono 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

Utilizzare l'operatore null coalescing: ??[C #] o If()[VB].

La scorciatoia per fornire un valore predefinito quando nullviene rilevato un:

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

Utilizzare l'operatore condizione null: ?.o ?[x]per array (disponibile in C # 6 e VB.NET 14):

Questo è talvolta chiamato anche navigazione sicura o Elvis (dopo la sua forma) operatore. Se l'espressione sul lato sinistro dell'operatore è null, il lato destro non verrà valutato e verrà invece restituito null. Ciò significa casi come questo:

var title = person.Title.ToUpper();

Se la persona non ha un titolo, ciò genererà un'eccezione perché sta provando a chiamare ToUpperuna proprietà con un valore nullo.

In C# 5e sotto, questo può essere protetto con:

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

Ora la variabile del titolo sarà nulla invece di generare un'eccezione. C # 6 introduce una sintassi più breve per questo:

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

Ciò comporterà la variabile del titolo essendo null, e la chiamata a ToUppernon viene effettuata se lo person.Titleè null.

Ovviamente, devi ancora verificare la presenza titledi null o utilizzare l'operatore condizione null insieme all'operatore null coalescing ( ??) per fornire un valore predefinito:

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

Allo stesso modo, per gli array è possibile utilizzare ?[i]come segue:

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

Questo farà quanto segue: Se myIntArrayè null, l'espressione restituisce null e puoi verificarla in sicurezza. Se contiene un array, farà lo stesso di: elem = myIntArray[i];e restituisce l' i<sup>th</sup>elemento.

Usa contesto null (disponibile in C # 8):

Introdotti in C# 8questo contesto, i tipi di riferimento null e nullble eseguono analisi statiche sulle variabili e forniscono un avviso del compilatore se un valore può essere potenzialmente nullo o se è stato impostato su null. I tipi di riferimento nullable consentono ai tipi di essere esplicitamente autorizzati a essere nulli.

Il contesto di annotazione nullable e il contesto di avviso nullable possono essere impostati per un progetto usando l' Nullableelemento nel csprojfile. Questo elemento configura il modo in cui il compilatore interpreta la nullità dei tipi e quali avvisi vengono generati. Le impostazioni valide sono:

  • abilita: il contesto delle annotazioni nullable è abilitato. Il contesto di avviso nullable è abilitato. Le variabili di un tipo di riferimento, ad esempio la stringa, non sono annullabili. Tutti gli avvisi di nullità sono abilitati.
  • disabilita: il contesto delle annotazioni nullable è disabilitato. Il contesto di avviso nullable è disabilitato. Le variabili di un tipo di riferimento sono ignare, proprio come le versioni precedenti di C #. Tutti gli avvisi di nullità sono disabilitati.
  • safeonly: il contesto di annotazione nullable è abilitato. Il contesto di avviso nullable è sicuro. Le variabili di un tipo di riferimento non sono annullabili. Tutti gli avvisi di nullità di sicurezza sono abilitati.
  • avvisi: il contesto delle annotazioni nullable è disabilitato. Il contesto di avviso nullable è abilitato. Le variabili di un tipo di riferimento sono ignare. Tutti gli avvisi di nullità sono abilitati.
  • safeonlywarnings: il contesto di annotazione nullable è disabilitato. Il contesto di avviso nullable è sicuro. Le variabili di un tipo di riferimento sono ignare. Tutti gli avvisi di nullità di sicurezza sono abilitati.

Un tipo di riferimento nullable viene notato usando la stessa sintassi dei tipi di valore nullable: a ?viene aggiunto al tipo di variabile.

Tecniche speciali per il debug e la correzione di null deref negli iteratori

C#supporta "blocchi iteratori" (chiamati "generatori" in alcune altre lingue popolari). Le eccezioni di dereferenza nulla possono essere particolarmente difficili da eseguire il debug nei blocchi iteratori a causa dell'esecuzione differita:

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

Se i whateverrisultati nullentrano, MakeFrobverranno lanciati. Ora, potresti pensare che la cosa giusta da fare sia questa:

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

Perché è sbagliato? Perché il blocco iteratore non viene eseguito fino a quando foreach! La chiamata a GetFrobsrestituisce semplicemente un oggetto che una volta eseguito eseguirà il blocco iteratore.

Scrivendo un controllo null come questo si impedisce la dereferenza null, ma si sposta l'eccezione dell'argomento null sul punto dell'iterazione , non sul punto della chiamata , e questo è molto confuso per il debug .

La correzione corretta è:

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

Ossia, crea un metodo di supporto privato con la logica del blocco iteratore e un metodo di superficie pubblica che esegua il controllo null e restituisca l'iteratore. Ora, quando GetFrobsviene chiamato, il controllo null GetFrobsForRealviene eseguito immediatamente e quindi viene eseguito quando la sequenza viene ripetuta.

Se si esamina l'origine di riferimento per gli LINQoggetti, si vedrà che questa tecnica è utilizzata dappertutto. Scrivere è leggermente più ingombrante, ma semplifica notevolmente il debug degli errori di nullità. Ottimizza il tuo codice per la comodità del chiamante, non per la comodità dell'autore .

Nota sulle null dereferenze in codice non sicuro

C#ha una modalità "non sicura" che è, come suggerisce il nome, estremamente pericolosa perché i normali meccanismi di sicurezza che forniscono la sicurezza della memoria e la sicurezza del tipo non sono applicati. Non dovresti scrivere codice non sicuro a meno che tu non abbia una conoscenza approfondita e profonda di come funziona la memoria .

In modalità non sicura, è necessario essere consapevoli di due fatti importanti:

  • la dereferenziazione di un puntatore null produce la stessa eccezione della dereferenziazione di un riferimento null
  • la dereferenziazione di un puntatore non nullo non valido può produrre tale eccezione in alcune circostanze

Per capire perché, aiuta in primo luogo a capire come .NET produce eccezioni di dereference nulle. (Questi dettagli si applicano a .NET in esecuzione su Windows; altri sistemi operativi utilizzano meccanismi simili.)

La memoria è virtualizzata in Windows; ogni processo ottiene uno spazio di memoria virtuale di molte "pagine" di memoria che vengono monitorate dal sistema operativo. Su ogni pagina di memoria sono presenti dei flag che determinano il modo in cui può essere utilizzato: letto, scritto, eseguito e così via. La pagina più bassa è contrassegnata come "produce un errore se mai usata in alcun modo".

Sia un puntatore null che un riferimento null in C#sono rappresentati internamente come il numero zero e quindi qualsiasi tentativo di dereferenziarlo nella sua memoria di archiviazione corrispondente provoca un errore nel sistema operativo. Il runtime .NET rileva quindi questo errore e lo trasforma nell'eccezione di dereference nulla.

Ecco perché la dereferenziazione sia di un puntatore null che di un riferimento null produce la stessa eccezione.

E il secondo punto? Dereferenziare qualsiasi puntatore non valido che cade nella pagina più bassa della memoria virtuale provoca lo stesso errore del sistema operativo e quindi la stessa eccezione.

Perché questo ha senso? Bene, supponiamo di avere una struttura contenente due ints e un puntatore non gestito uguale a null. Se tentiamo di dereferenziare il secondo int nella struttura, CLRnon tenterà di accedere alla memoria nella posizione zero; accederà alla memoria nella posizione quattro. Ma logicamente questa è una nullità perché stiamo arrivando a quell'indirizzo tramite il null.

Se stai lavorando con un codice non sicuro e ricevi un'eccezione di dereference nulla, tieni presente che il puntatore offensivo non deve essere null. Può essere qualsiasi posizione nella pagina più bassa e verrà prodotta questa eccezione.


55
Forse questo è un commento stupido, ma il primo e miglior modo per evitare questo problema sarebbe inizializzare l'oggetto? Per me se questo errore si verifica è di solito perché ho dimenticato di inizializzare qualcosa come l'elemento array. Penso che sia molto meno comune definire l'oggetto come null e quindi fare riferimento a esso. Forse dare il modo di risolvere ogni problema adiacente alla descrizione. Ancora un buon post.
JPK,

30
Cosa succede se non esiste alcun oggetto, ma piuttosto il valore restituito da un metodo o una proprietà?
John Saunders,

6
L'esempio del libro / autore è un po 'strano .... Come si compila? Come funziona anche l'intellisense? Cos'è questo non sono bravo con Computar ...

5
@Will: la mia ultima modifica aiuta? In caso contrario, si prega di essere più espliciti su ciò che si vede come un problema.
John Saunders,

6
@JohnSaunders Oh, no, scusa, intendevo la versione di inizializzazione dell'oggetto. new Book { Author = { Age = 45 } };In che modo anche l'inizializzazione interiore ... Non riesco a pensare a una situazione in cui l'init interiore avrebbe mai funzionato, ma si compila e l'intellisense funziona ... A meno che per le strutture?

311

Eccezione NullReference - Visual Basic

Il NullReference Exceptionper Visual Basic non è diverso da quello in C # . Dopotutto, entrambi segnalano la stessa eccezione definita in .NET Framework che utilizzano entrambi. Le cause uniche di Visual Basic sono rare (forse solo una).

Questa risposta utilizzerà i termini, la sintassi e il contesto di Visual Basic. Gli esempi utilizzati provengono da un gran numero di precedenti domande di overflow dello stack. Questo per massimizzare la pertinenza usando i tipi di situazioni spesso visti nei post. Una spiegazione in più è fornita anche per coloro che potrebbero averne bisogno. Un esempio simile al tuo è molto probabilmente elencato qui.

Nota:

  1. Questo è basato su concetti: non esiste un codice da incollare nel progetto. Ha lo scopo di aiutarti a capire cosa causa un NullReferenceException(NRE), come trovarlo, come risolverlo e come evitarlo. Un NRE può essere causato in molti modi, quindi è improbabile che questo sia il tuo unico incontro.
  2. Gli esempi (dai post Stack Overflow) non mostrano sempre il modo migliore per fare qualcosa in primo luogo.
  3. In genere, viene utilizzato il rimedio più semplice.

Significato di base

Il messaggio "Oggetto non impostato su un'istanza di Oggetto" indica che si sta tentando di utilizzare un oggetto che non è stato inizializzato. Questo si riduce a uno di questi:

  • Il codice ha dichiarato una variabile oggetto, ma non l'ha inizializzata (creare un'istanza o " istanziarla ")
  • Qualcosa che il tuo codice presumeva avrebbe inizializzato un oggetto, no
  • Forse, altro codice ha invalidato prematuramente un oggetto ancora in uso

Trovare la causa

Poiché il problema è un riferimento a un oggetto Nothing, la risposta è esaminarli per scoprire quale. Quindi determinare perché non è inizializzato. Tieni il mouse sulle varie variabili e Visual Studio (VS) mostrerà i loro valori - il colpevole sarà Nothing.

Visualizzazione debug IDE

È inoltre necessario rimuovere eventuali blocchi Try / Catch dal codice pertinente, in particolare quelli in cui non è presente nulla nel blocco Catch. Ciò causerà l'arresto anomalo del codice quando tenta di utilizzare un oggetto che lo è Nothing. Questo è quello che vuoi perché identificherà la posizione esatta del problema e ti permetterà di identificare l'oggetto che lo causa.

A MsgBoxnella cattura che viene visualizzato Error while...sarà di scarso aiuto. Questo metodo porta anche a pessime domande su Stack Overflow, poiché non è possibile descrivere l'eccezione effettiva, l'oggetto coinvolto o persino la riga di codice in cui si verifica.

Puoi anche usare Locals Window( Debug -> Windows -> Locals ) per esaminare i tuoi oggetti.

Una volta che sai qual è e dove si trova il problema, di solito è abbastanza facile da risolvere e più veloce di pubblicare una nuova domanda.

Guarda anche:

Esempi e rimedi

Oggetti classe / Creazione di un'istanza

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

Il problema è che Dimnon crea un oggetto CashRegister ; dichiara solo una variabile denominata regdi quel tipo. Dichiarare una variabile oggetto e creare un'istanza sono due cose diverse.

Rimedio

L' Newoperatore può spesso essere utilizzato per creare l'istanza quando viene dichiarata:

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

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

Quando è appropriato creare l'istanza in un secondo momento:

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

Nota: Non utilizzare Dimdi nuovo in una procedura, compreso il costruttore ( Sub New):

Private reg As CashRegister
'...

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

Questo creerà una variabile localereg , che esiste solo in quel contesto (sotto). La regvariabile a livello di modulo Scopeche verrà utilizzato resti in qualsiasi altro luogo Nothing.

L' Newoperatore mancante è la causa numero 1 delleNullReference Exceptions domande di overflow dello stack esaminate.

Visual Basic tenta di chiarire ripetutamente il processo utilizzando New: L' Newoperatore crea un nuovo oggetto e chiama Sub New- il costruttore - dove l'oggetto può eseguire qualsiasi altra inizializzazione.

Per essere chiari, Dim(o Private) dichiara solo una variabile e la sua Type. L' ambito della variabile, indipendentemente dal fatto che esista per l'intero modulo / classe o sia locale in una procedura, è determinato da dove viene dichiarato. Private | Friend | Publicdefinisce il livello di accesso, non Scope .

Per ulteriori informazioni, vedere:


Array

Le matrici devono inoltre essere istanziate:

Private arr as String()

Questo array è stato solo dichiarato, non creato. Esistono diversi modi per inizializzare un array:

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

Nota: a partire da VS 2010, quando si inizializza un array locale usando un valore letterale e Option Infer, gli elementi As <Type>e Newsono facoltativi:

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}

Il tipo di dati e la dimensione dell'array sono dedotti dai dati assegnati. Le dichiarazioni a livello di classe / modulo richiedono ancora As <Type>con Option Strict:

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

Esempio: matrice di oggetti di classe

Dim arrFoo(5) As Foo

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

L'array è stato creato, ma gli Foooggetti al suo interno no.

Rimedio

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

L'uso di a List(Of T)renderà abbastanza difficile avere un elemento senza un oggetto valido:

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

Per ulteriori informazioni, vedere:


Elenchi e raccolte

Anche le raccolte .NET (di cui esistono molte varietà - Elenchi, Dizionario, ecc.) Devono essere istanziate o create.

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

Ottieni la stessa eccezione per lo stesso motivo: è myListstata dichiarata solo, ma non è stata creata alcuna istanza. Il rimedio è lo stesso:

myList = New List(Of String)

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

Una svista comune è una classe che utilizza una raccolta 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

Entrambe le procedure si tradurranno in un NRE, poiché barListviene solo dichiarato, non istanziato. La creazione di un'istanza di Foonon creerà anche un'istanza dell'interno barList. Potrebbe essere stato l'intento di farlo nel costruttore:

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

Come prima, questo non è corretto:

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

Per ulteriori informazioni, vedere List(Of T)Classe .


Oggetti provider di dati

Lavorare con i database presenta molte opportunità per una NullReference perché ci possono essere molti oggetti ( Command, Connection, Transaction, Dataset, DataTable, DataRows....) in uso in una sola volta. Nota: non importa quale fornitore di dati stai usando - MySQL, SQL Server, OleDB, ecc. - i concetti sono gli stessi.

Esempio 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

Come prima, l' dsoggetto Dataset è stato dichiarato, ma non è mai stata creata un'istanza. Il DataAdapterriempirà uno esistente DataSet, non crearne uno. In questo caso, poiché dsè una variabile locale, l'IDE ti avverte che ciò potrebbe accadere:

img

Se dichiarato come variabile a livello di modulo / classe, come sembra essere il caso con, il compilatore non può sapere se l'oggetto è stato creato da una procedura a monte. Non ignorare gli avvisi.

Rimedio

Dim ds As New DataSet

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

Un errore di battitura è un problema qui: Employeesvs Employee. Non è stato DataTablecreato alcun nome "Dipendente", quindi i NullReferenceExceptionrisultati provano ad accedervi. Un altro potenziale problema sta presumendo Itemsche potrebbe non esserlo quando l'SQL include una clausola WHERE.

Rimedio

Poiché utilizza una tabella, l'utilizzo Tables(0)eviterà errori di ortografia. L'esame Rows.Countpuò anche aiutare:

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è una funzione che restituisce il numero di Rowsinteressati che può anche essere testato:

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

Esempio 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

Il DataAdapterfornirà TableNamescome mostrato nell'esempio precedente, ma non è così i nomi di parsing dalla tabella SQL o database. Di conseguenza, fa ds.Tables("TICKET_RESERVATION")riferimento a una tabella inesistente.

Il rimedio è lo stesso, fare riferimento alla tabella per indice:

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

Vedi anche Classe DataTable .


Percorsi oggetto / nidificati

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

Il codice sta testando solo Itemsentrambi myFooe Barpotrebbe anche essere Nothing. Il rimedio è testare l'intera catena o percorso di oggetti uno alla volta:

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

AndAlsoè importante. I test successivi non verranno eseguiti una volta Falseriscontrata la prima condizione. Ciò consente al codice di "eseguire il drill" in modo sicuro nell'oggetto (i) un "livello" alla volta, valutando myFoo.Barsolo dopo (e se) che myFooè stato ritenuto valido. Le catene di oggetti o i percorsi possono diventare piuttosto lunghi quando si codificano oggetti complessi:

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

Non è possibile fare riferimento a nulla "a valle" di un nulloggetto. Questo vale anche per i controlli:

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

Qui, myWebBrowsero Documentpotrebbe essere Nothing o l' formfld1elemento potrebbe non esistere.


Controlli dell'interfaccia utente

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)

Tra le altre cose, questo codice non prevede che l'utente potrebbe non aver selezionato qualcosa in uno o più controlli dell'interfaccia utente. ListBox1.SelectedItempotrebbe anche essere Nothing, quindi ListBox1.SelectedItem.ToStringsi tradurrà in un NRE.

Rimedio

Convalida i dati prima di utilizzarli (usa anche Option Stricti parametri 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

In alternativa, puoi usare (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Moduli di Visual Basic

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

Questo è un modo abbastanza comune per ottenere un NRE. In C #, a seconda di come è codificato, l'IDE riporterà che Controlsnon esiste nel contesto corrente o "non può fare riferimento a un membro non statico". Quindi, in una certa misura, questa è solo una situazione di VB. È anche complesso perché può causare una cascata di guasti.

Le matrici e le raccolte non possono essere inizializzate in questo modo. Questo codice di inizializzazione verrà eseguito prima che il costruttore crei il Formo il Controls. Di conseguenza:

  • Le liste e la raccolta saranno semplicemente vuote
  • L'array conterrà cinque elementi di Nothing
  • L' somevarassegnazione si tradurrà in un NRE immediato perché Nothing non ha una .Textproprietà

Il riferimento successivo agli elementi dell'array genererà un NRE. Se lo fai in Form_Load, a causa di un bug strano, l'IDE potrebbe non segnalare l'eccezione quando si verifica. L'eccezione verrà visualizzata in seguito quando il codice tenta di utilizzare l'array. Questa "eccezione silenziosa" è dettagliata in questo post . Per i nostri scopi, la chiave è che quando si verifica qualcosa di catastrofico durante la creazione di un modulo ( Sub Newo Form Loadevento), le eccezioni potrebbero non essere segnalate, il codice esce dalla procedura e visualizza semplicemente il modulo.

Poiché nessun altro codice nel tuo Sub Newo Form Loadevento verrà eseguito dopo NRE, molte altre cose possono essere lasciate non inizializzate.

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

Nota che questo vale per tutti i controlli e tutti i riferimenti ai componenti che li rendono illegali dove sono:

Public Class Form1

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

Rimedio parziale

E 'curioso che VB non fornisce un avvertimento, ma il rimedio è quello di dichiarare i contenitori a livello di modulo, ma inizializzare loro in gestione dell'evento load forma quando i controlli fanno esistere. Questo può essere fatto Sub Newfintanto che il codice è dopo la InitializeComponentchiamata:

' 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

Il codice dell'array potrebbe non essere ancora fuori dal comune. Qualsiasi controllo che si trova in un controllo contenitore (come un GroupBoxo Panel) non verrà trovato in Me.Controls; saranno nella raccolta Controlli di quel Pannello o GroupBox. Né verrà restituito un controllo quando il nome del controllo è errato ( "TeStBox2"). In tali casi, Nothingverrà nuovamente memorizzato in quegli elementi dell'array e si verificherà un NRE quando si tenta di fare riferimento a esso.

Questi dovrebbero essere facili da trovare ora che sai cosa stai cercando: VS ti mostra l'errore dei tuoi modi

"Button2" risiede su a Panel

Rimedio

Invece di riferimenti indiretti per nome utilizzando la Controlsraccolta del modulo , utilizzare il riferimento di controllo:

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

Funzione che non restituisce nulla

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

Questo è un caso in cui l'IDE ti avvertirà che " non tutti i percorsi restituiscono un valore e un NullReferenceExceptionrisultato può essere ". È possibile eliminare l'avviso sostituendolo Exit Functioncon Return Nothing, ma ciò non risolve il problema. Tutto ciò che tenta di utilizzare il ritorno quando someCondition = Falsesi tradurrà in un NRE:

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

Rimedio

Sostituire Exit Functionnella funzione con Return bList. Restituire un vuoto List non equivale a restituire Nothing. Se esiste la possibilità che un oggetto restituito possa essere Nothing, provare prima di usarlo:

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

Try / Catch implementato male

Un Try / Catch mal implementato può nascondere dove si trova il problema e provocarne di nuovi:

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

Questo è un caso in cui un oggetto non viene creato come previsto, ma dimostra anche la controutilità di un vuoto Catch.

C'è una virgola aggiuntiva nell'SQL (dopo 'mailaddress') che si traduce in un'eccezione in .ExecuteReader. Dopo Catchche non fa nulla, Finallytenta di eseguire la pulizia, ma poiché non è possibile Closeun DataReaderoggetto null , si NullReferenceExceptionottengono risultati nuovi di zecca .

Un Catchblocco vuoto è il parco giochi del diavolo. Questo OP era sconcertato dal motivo per cui stava ottenendo un NRE nel Finallyblocco. In altre situazioni, un vuoto Catchpuò causare qualcos'altro molto più a valle che va in tilt e farti passare il tempo a guardare le cose sbagliate nel posto sbagliato per il problema. (La "eccezione silenziosa" sopra descritta fornisce lo stesso valore di intrattenimento.)

Rimedio

Non utilizzare blocchi Try / Catch vuoti: lasciare che il codice si blocchi in modo da poter a) identificare la causa b) identificare la posizione ec) applicare un rimedio adeguato. I blocchi Try / Catch non hanno lo scopo di nascondere le eccezioni alla persona qualificata in modo univoco per risolverli: lo sviluppatore.


DBNull non è lo stesso di Nothing

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

La IsDBNullfunzione viene utilizzata per verificare se un valore è uguale a System.DBNull: Da MSDN:

Il valore System.DBNull indica che l'Oggetto rappresenta dati mancanti o inesistenti. DBNull non è uguale a Nothing, il che indica che una variabile non è stata ancora inizializzata.

Rimedio

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

Come prima, puoi provare per Nothing, quindi per un valore specifico:

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

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

FirstOrDefaultrestituisce il primo elemento o il valore predefinito, che è Nothingper i tipi di riferimento e mai DBNull:

If getFoo IsNot Nothing Then...

controlli

Dim chk As CheckBox

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

Se un CheckBoxcon chkNamenon può essere trovato (o esiste in a GroupBox), allora chksarà Nothing e il tentativo di fare riferimento a qualsiasi proprietà comporterà un'eccezione.

Rimedio

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

DataGridView

Il DGV presenta alcune stranezze periodicamente:

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"

Se dgvBooksha AutoGenerateColumns = True, creerà le colonne, ma non li nome, in modo che il codice di cui sopra non riesce quando li fa riferimento al nome.

Rimedio

Denominare le colonne manualmente o fare riferimento per indice:

dgvBooks.Columns(0).Visible = True

Esempio 2 - Attenzione al 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

Quando il tuo DataGridViewè AllowUserToAddRowscome True(impostazione predefinita), Cellsconterrà la riga vuota / nuova in fondo Nothing. La maggior parte dei tentativi di utilizzare i contenuti (ad esempio ToString) comporterà un NRE.

Rimedio

Utilizzare un For/Eachciclo e testare la IsNewRowproprietà per determinare se si tratta dell'ultima riga. Questo funziona se AllowUserToAddRowsè vero o no:

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

Se si utilizza un For nciclo, modificare il conteggio delle righe o utilizzare Exit Forquando IsNewRowè vero.


My.Settings (StringCollection)

In determinate circostanze, il tentativo di utilizzare un elemento da My.Settingscui è StringCollectionpossibile può comportare un riferimento null la prima volta che lo si utilizza. La soluzione è la stessa, ma non così ovvia. Prendere in considerazione:

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

Poiché VB gestisce le impostazioni per te, è ragionevole aspettarsi che inizializzi la raccolta. Lo farà, ma solo se in precedenza hai aggiunto una voce iniziale alla raccolta (nell'editor delle impostazioni). Poiché la raccolta viene (apparentemente) inizializzata quando viene aggiunto un elemento, rimane Nothingquando non ci sono elementi nell'editor Impostazioni da aggiungere.

Rimedio

Inizializza la raccolta delle impostazioni nel Loadgestore eventi del modulo , se / quando necessario:

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

In genere, la Settingsraccolta dovrà essere inizializzata solo al primo avvio dell'applicazione. Un rimedio alternativo è aggiungere un valore iniziale alla tua raccolta in Progetto -> Impostazioni | FooBars , salva il progetto, quindi rimuovi il valore falso.


Punti chiave

Probabilmente hai dimenticato l' Newoperatore.

o

Qualcosa che hai assunto avrebbe funzionato in modo impeccabile per restituire un oggetto inizializzato al tuo codice, non lo ha fatto.

Non ignorare gli avvisi del compilatore (mai) e utilizzare Option Strict On(sempre).


Eccezione MSull NullReference


226

Un altro scenario è quando si lancia un oggetto null in un tipo di valore . Ad esempio, il codice seguente:

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

Lancerà a NullReferenceExceptionsul cast. Sembra abbastanza ovvio nell'esempio precedente, ma ciò può accadere in scenari complessi più "late-binding" in cui l'oggetto null è stato restituito da un codice che non si possiede e il cast è ad esempio generato da un sistema automatico.

Un esempio di questo è questo semplice frammento di associazione ASP.NET con il controllo Calendar:

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

Qui, SelectedDatein effetti , è una proprietà - di DateTimetipo - del Calendartipo di controllo Web e l'associazione potrebbe restituire perfettamente qualcosa di nullo. Il generatore ASP.NET implicito creerà un pezzo di codice che sarà equivalente al codice cast sopra. E questo genererà un problema NullReferenceExceptionche è abbastanza difficile da individuare, perché risiede nel codice generato da ASP.NET che compila bene ...


7
Ottima cattura. Modo da evitare per una sola riga:DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Serge Shultz,

160

Significa che la variabile in questione non punta a nulla. Potrei generarlo così:

SqlConnection connection = null;
connection.Open();

Ciò genererà l'errore perché mentre ho dichiarato la variabile " connection", non ha puntato a nulla. Quando provo a chiamare il membro " Open", non c'è alcun riferimento per la sua risoluzione e genererà l'errore.

Per evitare questo errore:

  1. Inizializza sempre i tuoi oggetti prima di provare a fare qualcosa con loro.
  2. Se non sei sicuro che l'oggetto sia nullo, controllalo con object == null.

Lo strumento Resharper di JetBrains identificherà ogni posizione nel codice che ha la possibilità di un errore di riferimento null, consentendoti di effettuare un controllo null. Questo errore è la fonte numero uno di bug, IMHO.


3
Lo strumento Resharper di JetBrains identificherà ogni posizione nel codice che ha la possibilità di un errore di riferimento null. Questo non è corretto Ho una soluzione senza quel rilevamento, ma il codice risulta occasionalmente all'eccezione. Sospetto che occasionalmente non sia rilevabile - almeno da parte loro - quando è coinvolto il multithreading, ma non posso commentare ulteriormente perché non ho ancora identificato la posizione del mio bug.
j riv

Ma come risolverlo quando NullReferenceException arriva in usign HttpContext.Current.Responce.Clear (). Non viene risolto da nessuna delle soluzioni di cui sopra. perché durante la creazione del suo oggetto oggetto di HttpContext si verifica un errore "Risoluzione di sovraccarico non riuscita perché nessun" Nuovo "accessibile accetta questo numero di argomenti.
Sunny Sandeep

158

Significa che il tuo codice ha utilizzato una variabile di riferimento all'oggetto impostata su null (ovvero non ha fatto riferimento a un'istanza effettiva dell'oggetto).

Per evitare l'errore, gli oggetti che potrebbero essere nulli devono essere testati per null prima di essere utilizzati.

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
}

96

Tieni presente che, indipendentemente dallo scenario, la causa è sempre la stessa in .NET:

Stai tentando di utilizzare una variabile di riferimento il cui valore è Nothing/ null. Quando il valore è Nothing/ nullper la variabile di riferimento, significa che in realtà non contiene un riferimento a un'istanza di alcun oggetto esistente nell'heap.

Non hai mai assegnato qualcosa alla variabile, non hai mai creato un'istanza del valore assegnato alla variabile o hai impostato la variabile uguale a Nothing/ nullmanualmente o hai chiamato una funzione che imposta la variabile su Nothing/ nullper te.


87

Un esempio di questa eccezione generata è: quando si tenta di controllare qualcosa, questo è nullo.

Per esempio:

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
} 

Il runtime .NET genererà una NullReferenceException quando si tenta di eseguire un'azione su qualcosa che non è stato istanziato, ad esempio il codice sopra.

In confronto a una ArgumentNullException che viene generalmente lanciata come misura difensiva se un metodo prevede che ciò che gli viene passato non sia nullo.

Ulteriori informazioni sono disponibili in N # NullReferenceException e Null Parameter .


87

Aggiornamento C # 8.0, 2019: tipi di riferimento nullable

C # 8.0 introduce tipi di riferimento annullabili e tipi di riferimento non annullabili . Pertanto, per evitare una NullReferenceException, è necessario verificare solo i tipi di riferimento nullable .


Se non è stato inizializzato un tipo di riferimento e si desidera impostare o leggere una delle sue proprietà, verrà generata un'eccezione NullReferenceException .

Esempio:

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

Puoi semplicemente evitarlo controllando se la variabile non è nulla:

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

Per comprendere appieno il motivo per cui viene generata una NullReferenceException, è importante conoscere la differenza tra tipi di valore e [tipi di riferimento] [3].

Quindi, se hai a che fare con tipi di valore , non possono verificarsi NullReferenceExceptions . Tuttavia, devi stare attento quando hai a che fare con i tipi di riferimento !

Solo i tipi di riferimento, come suggerisce il nome, possono contenere riferimenti o puntare letteralmente a nulla (o "null"). Considerando che i tipi di valore contengono sempre un valore.

Tipi di riferimento (questi devono essere controllati):

  • dinamico
  • oggetto
  • corda

Tipi di valore (puoi semplicemente ignorare questi):

  • Tipi numerici
  • Tipi integrali
  • Tipi a virgola mobile
  • decimale
  • bool
  • Strutture definite dall'utente

6
-1: poiché la domanda è "Cos'è una NullReferenceException", i tipi di valore non sono rilevanti.
John Saunders,

21
@ John Saunders: non sono d'accordo. Come sviluppatore di software è davvero importante essere in grado di distinguere tra valore e tipi di riferimento. altrimenti le persone finiranno per verificare se i numeri interi sono nulli.
Fabian Bigler,

5
È vero, non solo nel contesto di questa domanda.
John Saunders,

4
Grazie per il suggerimento. L'ho migliorato un po 'e ho aggiunto un esempio in alto. Penso ancora che sia utile menzionare Tipi di riferimento e valore.
Fabian Bigler,

5
Penso che non hai aggiunto nulla che non fosse nelle altre risposte, poiché la domanda presuppone un tipo di riferimento.
John Saunders,

78

Un altro caso in cui NullReferenceExceptionspuò accadere è l'uso (errato) asdell'operatore :

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

Qui, Booke Carsono tipi incompatibili; a Carnon può essere convertito / cast in a Book. Quando questo cast fallisce, asritorna null. L'uso mybookdopo ciò causa aNullReferenceException .

In generale, dovresti usare un cast o as, come segue:

Se ti aspetti che la conversione del tipo abbia sempre successo (ad es. Sai quale oggetto dovrebbe essere in anticipo), allora dovresti usare un cast:

ComicBook cb = (ComicBook)specificBook;

Se non si è sicuri del tipo, ma si desidera provare a utilizzarlo come tipo specifico, utilizzare as:

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

2
Questo può accadere molto quando si deseleziona una variabile. Trovo che accada spesso nei gestori di eventi dopo aver modificato il tipo di elemento dell'interfaccia utente ma dimentico di aggiornare il code-behind.
Brendan,

65

Si sta utilizzando l'oggetto che contiene il riferimento di valore null. Quindi sta dando un'eccezione nulla. Nell'esempio il valore della stringa è nullo e durante il controllo della sua lunghezza si è verificata l'eccezione.

Esempio:

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

L'errore di eccezione è:

Eccezione non gestita:

System.NullReferenceException: riferimento all'oggetto non impostato su un'istanza di un oggetto. at Program.Main ()


1
Quanto profondo! Non ho mai considerato la costante "nulla" un valore di riferimento. Quindi è così che C # estrae un "NullPointer" eh? B / c come ricordo in C ++, un NPE può essere causato dereferenziando un puntatore non inizializzato (ovvero, tipo ref in c #) il cui valore predefinito è un indirizzo che non è assegnato a quel processo (molti casi sarebbe 0, specialmente nelle versioni successive di C ++ che eseguivano l'inizializzazione automatica, che appartiene al SO - f con esso e muore beeotch (o semplicemente cattura il sigkill con cui il SO attacca il tuo processo)).
Samis,

64

Mentre ciò che causa un NullReferenceExceptions e gli approcci per evitare / correggere tale eccezione sono stati affrontati in altre risposte, ciò che molti programmatori non hanno ancora imparato è come eseguire il debug in modo indipendente di tali eccezioni durante lo sviluppo.

In Visual Studio questo è generalmente facile grazie al debugger di Visual Studio .


Innanzitutto, assicurati che l'errore corretto venga rilevato - vedi Come posso consentire l'interruzione di "System.NullReferenceException" in VS2010? Nota 1

Quindi iniziare con il debug (F5) o collegare [il debugger VS] al processo in esecuzione . A volte può essere utile da usareDebugger.Break , che richiederà di avviare il debugger.

Ora, quando viene generata (o non gestita) NullReferenceException, il debugger si interromperà (ricordi la regola impostata sopra?) Sulla riga in cui si è verificata l'eccezione. A volte l'errore sarà facile da individuare.

Ad esempio, nella riga seguente l'unico codice che può causare l'eccezione è se viene myStringvalutato su null. Questo può essere verificato guardando la Finestra di controllo o eseguendo espressioni nella Finestra immediata .

var x = myString.Trim();

In casi più avanzati, come i seguenti, è necessario utilizzare una delle tecniche sopra (Guarda o Windows immediato) per ispezionare le espressioni per determinare se str1era nullo o se str2era nullo.

var x = str1.Trim() + str2.Trim();

Una volta in cui l'eccezione è a due è stato localizzato, di solito banale ragione a ritroso per scoprire dove il valore null è stato [errato] ha introdotto -

Prenditi il ​​tempo necessario per capire la causa dell'eccezione. Controlla le espressioni null. Ispeziona le espressioni precedenti che potrebbero aver provocato espressioni nulle. Aggiungi punti di interruzione ed esegui il programma come appropriato. Usa il debugger.


1 Se Break on Throws è troppo aggressivo e il debugger si arresta su un NPE nella libreria .NET o di terze parti, Break on User-Unhandled può essere utilizzato per limitare le eccezioni rilevate. Inoltre, VS2012 introduce Just My Code che consiglio anche di abilitare.

Se si esegue il debug con Just My Code abilitato, il comportamento è leggermente diverso. Con Just My Code abilitato, il debugger ignora le eccezioni di Common Language Runtime (CLR) di prima possibilità che vengono generate all'esterno di My Code e non passano attraverso My Code


59

Simon Mourier ha dato questo esempio :

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

dove una conversione unboxing (cast) da object (o da una delle classi System.ValueTypeo System.Enum, o da un tipo di interfaccia) a un tipo di valore (diverso da Nullable<>) in sé fornisceNullReferenceException .

Nell'altra direzione, una conversione di boxe da un Nullable<>che è HasValueuguale false a un tipo di riferimento, può dare un nullriferimento che può poi portare a NullReferenceException. L'esempio classico è:

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

A volte la boxe avviene in un altro modo. Ad esempio con questo metodo di estensione non generico:

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

il seguente codice sarà problematico:

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

Questi casi sorgono a causa delle regole speciali utilizzate dal runtime durante le Nullable<>istanze di inscatolamento .


42

Aggiunta di un caso in cui il nome della classe per l'entità utilizzata nel framework dell'entità è uguale al nome della classe per un file code-behind del modulo Web.

Supponiamo di avere un modulo Web Contact.aspx la cui codebehind classe è Contact e di avere un nome di entità Contact.

Quindi il codice seguente genererà una NullReferenceException quando si chiama context.SaveChanges ()

Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line

Per completezza della classe DataContext

public class DataContext : DbContext 
{
    public DbSet<Contact> Contacts {get; set;}
}

e contattare la classe di entità. A volte le classi di entità sono classi parziali in modo da poterle estendere anche in altri file.

public partial class Contact 
{
    public string Name {get; set;}
}

L'errore si verifica quando sia l'entità che la classe codebehind si trovano nello stesso spazio dei nomi. Per risolvere questo problema, rinominare la classe entità o la classe codebehind per Contact.aspx.

Motivo Non sono ancora sicuro del motivo. Ma ogni qualvolta una delle entità della classe estenderà System.Web.UI.Page questo errore si verifica.

Per la discussione dai un'occhiata a NullReferenceException in DbContext.saveChanges ()


41

Un altro caso generale in cui si potrebbe ricevere questa eccezione riguarda le classi beffardo durante i test delle unità. Indipendentemente dal framework di derisione utilizzato, è necessario assicurarsi che tutti i livelli appropriati della gerarchia di classi siano correttamente derisi. In particolare, tutte le proprietà a HttpContextcui fa riferimento il codice in prova devono essere derise.

Vedere " NullReferenceException generata durante il test di AuthorizationAttribute personalizzato " per un esempio un po 'dettagliato.


40

Ho una prospettiva diversa per rispondere a questo. Questo tipo di risposte "cos'altro posso fare per evitarlo? "

Quando si lavora su diversi livelli , ad esempio in un'applicazione MVC, un controller ha bisogno di servizi per chiamare le operazioni aziendali. In tali scenari, è possibile utilizzare il contenitore di iniezione di dipendenza per inizializzare i servizi per evitare NullReferenceException . Ciò significa che non è necessario preoccuparsi di verificare la presenza di null e chiamare semplicemente i servizi dal controller come se fossero sempre disponibili (e inizializzati) come singleton o prototipo.

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

6
-1: questo gestisce solo un singolo scenario: quello delle dipendenze non inizializzate. Questo è uno scenario di minoranza per NullReferenceException. La maggior parte dei casi è un semplice fraintendimento di come funzionano gli oggetti. Successivamente le più frequenti sono altre situazioni in cui lo sviluppatore ha ipotizzato che l'oggetto sarebbe stato inizializzato automaticamente.
John Saunders,

L'iniezione di dipendenza non viene generalmente utilizzata per evitare NullReferenceException. Non credo che tu abbia trovato uno scenario generale qui. In ogni caso, se modifichi la tua risposta per renderla più nello stile di stackoverflow.com/a/15232518/76337 , rimuoverò il voto negativo.
John Saunders,

38

Sulla questione di "cosa dovrei fare al riguardo" , ci possono essere molte risposte.

Un modo più "formale" di prevenire tali condizioni di errore durante lo sviluppo è applicare il design per contratto nel codice. Ciò significa che è necessario impostare invarianti di classe e / o persino precondizioni di funzione / metodo e postcondizioni sul proprio sistema durante lo sviluppo.

In breve, invarianti di classe assicurano che ci saranno alcuni vincoli nella tua classe che non verranno violati nell'uso normale (e quindi la classe non entrerà in uno stato incoerente). Presupposti indicano che i dati forniti come input per una funzione / metodo devono seguire alcuni set di vincoli e non violarli mai , e postcondizioni significano che un output di funzione / metodo deve seguire nuovamente i vincoli impostati senza mai violarli. Le condizioni del contratto non devono mai essere violate durante l'esecuzione di un programma privo di bug, pertanto la progettazione per contratto è verificata in pratica in modalità debug, mentre è disabilitata nelle versioni , per massimizzare le prestazioni del sistema sviluppato.

In questo modo, è possibile evitare NullReferenceExceptioncasi che sono il risultato della violazione dei vincoli impostati. Ad esempio, se si utilizza una proprietà oggetto Xin una classe e successivamente si tenta di invocare uno dei suoi metodi e Xha un valore nullo, ciò comporterà NullReferenceException:

public X { get; set; }

public void InvokeX()
{
    X.DoSomething(); // if X value is null, you will get a NullReferenceException
}

Ma se si imposta "la proprietà X non deve mai avere un valore nullo" come condizione preliminare del metodo, è possibile impedire lo scenario descritto in precedenza:

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

Per questa causa, esiste un progetto di Contratti di codice per le applicazioni .NET.

In alternativa, la progettazione per contratto può essere applicata mediante asserzioni .

AGGIORNARE: Vale la pena ricordare che il termine è stato coniato da Bertrand Meyer in relazione al suo design del linguaggio di programmazione Eiffel .


2
Ho pensato di aggiungere questo dato che nessuno lo ha menzionato e, per quanto esiste come approccio, la mia intenzione era quella di arricchire l'argomento.
Nick Louloudakis,

2
Grazie per aver arricchito l'argomento. Ho espresso la mia opinione sulla tua aggiunta. Ora altri possono fare lo stesso.
John Saunders,

2
Ho pensato che fosse una valida aggiunta all'argomento dato che si tratta di un thread molto apprezzato. Ho già sentito parlare di contratti con codice e questo è stato un buon promemoria per considerare di usarli.
VoteCoffee

36

A NullReferenceExceptionviene generato quando si tenta di accedere alle proprietà di un oggetto null o quando un valore di stringa diventa vuoto e si tenta di accedere ai metodi di stringa.

Per esempio:

  1. Quando si accede a un metodo stringa di una stringa vuota:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  2. Quando si accede a una proprietà di un oggetto null:

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

2
Questo non è corretto String.Empty.ToLower()non genererà un'eccezione di riferimento null. Rappresenta una stringa effettiva, sebbene vuota (es "".). Dato che questo ha un oggetto da chiamare ToLower(), non avrebbe senso lanciare un'eccezione di riferimento null lì.
Kjartan,

31

TL; DR: prova a utilizzare Html.Partialinvece diRenderpage


Stavo ottenendo Object reference not set to an instance of an objectquando ho provato a eseguire il rendering di una vista in una vista inviandola un modello, in questo modo:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

Il debug ha mostrato che il modello era Null in MyOtherView. Fino a quando non l'ho cambiato in:

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

E ha funzionato.

Inoltre, il motivo per cui non dovevo Html.Partialcominciare era perché a volte Visual Studio lanciava linee ondulate che sembravano errori Html.Partialse si trova all'interno di un foreachciclo costruito diversamente , anche se non è davvero un errore:

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

Ma sono stato in grado di eseguire l'applicazione senza problemi con questo "errore". Sono stato in grado di sbarazzarmi dell'errore modificando la struttura del foreachloop in questo modo:

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

Anche se ho la sensazione che sia stato perché Visual Studio ha letto male le e commerciali e le parentesi.


Volevi Html.Partial, non@Html.Partial
John Saunders

Inoltre, mostra quale riga ha generato l'eccezione e perché.
John Saunders,

L'errore si è verificato in MyOtherView.cshtml, che non ho incluso qui, perché il modello non è stato inviato correttamente (lo era Null), quindi sapevo che l'errore riguardava il modo in cui lo stavo inviando.
Travis Heeter

22

Cosa puoi fare al riguardo?

Ci sono molte buone risposte qui che spiegano cos'è un riferimento null e come eseguirne il debug. Ma c'è molto poco su come prevenire il problema o almeno renderlo più facile da rilevare.

Controlla gli argomenti

Ad esempio, i metodi possono controllare i diversi argomenti per vedere se sono nulli e generare un ArgumentNullException, un'eccezione ovviamente creata per questo preciso scopo.

Il costruttore per il ArgumentNullExceptionpari prende il nome del parametro e un messaggio come argomenti in modo da poter dire esattamente allo sviluppatore qual è il problema.

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

Usa gli strumenti

Ci sono anche diverse librerie che possono aiutare. "Resharper", ad esempio, può fornire avvisi durante la scrittura del codice, soprattutto se si utilizza il loro attributo: NotNullAttribute

Esistono "Contratti di codice Microsoft" in cui si utilizza la sintassi, ad esempio, Contract.Requires(obj != null)che consente di verificare il tempo di esecuzione e la compilazione: Presentazione dei contratti di codice .

C'è anche "PostSharp" che ti permetterà di usare solo attributi come questo:

public void DoSometing([NotNull] obj)

In questo modo e rendendo PostSharp parte del processo di compilazione objverrà verificato null durante l'esecuzione. Vedi: PostSharp null check

Soluzione di codice semplice

Oppure puoi sempre codificare il tuo approccio usando un semplice vecchio codice. Ad esempio, ecco una struttura che puoi usare per catturare riferimenti null. È modellato sullo stesso concetto di Nullable<T>:

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

Useresti molto simile allo stesso modo in cui useresti Nullable<T>, tranne con l'obiettivo di ottenere esattamente il contrario - per non permettere null. Ecco alcuni esempi:

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T>viene implicitamente trasmesso da e verso in Tmodo da poterlo utilizzare praticamente ovunque tu ne abbia bisogno. Ad esempio, è possibile passare un Personoggetto a un metodo che accetta un NotNull<Person>:

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

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

Come puoi vedere sopra come con nullable, accedi al valore sottostante tramite la Valueproprietà. In alternativa, puoi utilizzare un cast esplicito o implicito, puoi vedere un esempio con il valore restituito di seguito:

Person person = GetPerson();

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

Oppure puoi anche usarlo quando il metodo ritorna T(in questo caso Person) semplicemente eseguendo un cast. Ad esempio, il seguente codice vorrebbe solo il codice sopra:

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

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

Combina con l'estensione

Combina NotNull<T>con un metodo di estensione e puoi coprire ancora più situazioni. Ecco un esempio di come può apparire il metodo di estensione:

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

Ed ecco un esempio di come potrebbe essere usato:

var person = GetPerson().NotNull();

GitHub

Per tuo riferimento ho reso disponibile il codice sopra su GitHub, puoi trovarlo su:

https://github.com/luisperezphd/NotNull

Funzionalità della lingua correlata

C # 6.0 ha introdotto l '"operatore null condizionale" che aiuta un po' in questo. Con questa funzione, puoi fare riferimento a oggetti nidificati e se uno di essi è nulll'intera espressione restituita null.

Ciò riduce il numero di controlli null da eseguire in alcuni casi. La sintassi consiste nel mettere un punto interrogativo prima di ogni punto. Prendi ad esempio il seguente codice:

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

Immagina che countrysia un oggetto di tipo Countryche ha una proprietà chiamata Statee così via. Se country, State, County, o Cityè nullquindi address will benullo . Therefore you only have to check whetherindirizzo isnull`.

È una grande funzionalità, ma ti dà meno informazioni. Non è ovvio quale dei 4 sia nullo.

Incorporato come Nullable?

C # ha una bella scorciatoia per Nullable<T>, puoi rendere nulla nullable inserendo un punto interrogativo dopo il tipo in questo modo int?.

Sarebbe bello se C # avuto qualcosa di simile alla NotNull<T>struct sopra e aveva una stenografia simile, forse il punto esclamativo in modo che si potrebbe scrivere qualcosa di simile (!): public void WriteName(Person! person).


2
Non lanciare mai NullReferenceException
John Saunders il

@JohnSaunders oso chiedere perché? (Scherzi a parte perché?)
Luis Perez

2
NullReferenceException deve essere generata dal CLR. Significa che si è verificato un riferimento a un null. Ciò non significa che si verificherebbe un riferimento a un null, tranne per il fatto che per prima cosa hai abilmente controllato.
John Saunders,

Vedo il tuo punto su come sarebbe confuso. L'ho aggiornato con un'eccezione regolare per questo esempio e un'eccezione personalizzata in GitHub.
Luis Perez

Ottima risposta per una domanda così basilare. Non è così male quando è il tuo codice che non riesce. È orribile quando proviene da una biblioteca commerciale di terze parti su cui fai affidamento e l'assistenza clienti continua a insistere sul fatto che deve essere il tuo codice a causare il problema. E tu non sei del tutto sicuro che non lo sia e l'intero progetto è destinato a fermarsi .. In realtà penso che questo potrebbe costituire un epitaffio appropriato per la mia lapide: "Il riferimento all'oggetto non è impostato su un'istanza di un oggetto".
Darrel Lee,

10

È interessante notare che nessuna delle risposte in questa pagina menziona i due casi limite, spero che nessuno se ne occupi se li aggiungo:

Edge case n. 1: accesso simultaneo a un dizionario

I dizionari generici in .NET non sono thread-safe e a volte potrebbero lanciare NullReferenceo addirittura (più frequente) aKeyNotFoundException quando si tenta di accedere a una chiave da due thread simultanei. L'eccezione è abbastanza fuorviante in questo caso.

Edge case # 2: codice non sicuro

Se a NullReferenceExceptionviene generato un unsafecodice, è possibile esaminare le variabili del puntatore e verificarleIntPtr.Zero o altro. Che è la stessa cosa ("eccezione puntatore nullo"), ma in un codice non sicuro, le variabili vengono spesso espresse in tipi di valore / array, ecc. E si sbatte la testa contro il muro, chiedendosi come un tipo di valore può lanciare questo eccezione.

(Un altro motivo per non usare codice non sicuro a meno che tu non ne abbia bisogno, comunque)


5
Il tuo esempio di dizionario non è un caso limite. Se l'oggetto non è thread-safe, l'utilizzo di più thread produce risultati casuali. Il tuo esempio di codice non sicuro differisce da nullin che modo?
John Saunders,

10

È possibile correggere NullReferenceException in modo pulito utilizzando gli operatori Null-conditional in c # 6 e scrivere meno codice per gestire i controlli null.

Viene utilizzato per verificare la presenza di null prima di eseguire un'operazione di accesso (?.) O indice (? [) Del membro.

Esempio

  var name = p?.Spouse?.FirstName;

è equivalente a:

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }

Il risultato è che il nome sarà null quando p è null o quando p.Spouse è null.

In caso contrario, al nome della variabile verrà assegnato il valore di p.Spouse.FirstName.

Per maggiori dettagli: Operatori nulli


9

La riga di errore "Riferimento oggetto non impostato su un'istanza di un oggetto" indica che non è stato assegnato un oggetto istanza a un riferimento oggetto e si sta ancora accedendo a proprietà / metodi di quell'oggetto.

per esempio: supponiamo che tu abbia una classe chiamata myClass e contenga una proprietà prop1.

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

Ora stai accedendo a questo prop1 in qualche altra classe proprio come sotto:

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref.prop1 = 1;  //This line throws error
     }
}

la riga sopra genera errore perché il riferimento della classe myClass è dichiarato ma non istanziato o un'istanza di oggetto non è assegnata al riferimento di quella classe.

Per risolvere questo problema è necessario creare un'istanza (assegnare l'oggetto al riferimento di quella classe).

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;  
     }
}

4

Il riferimento NullReferenceException o Object non impostato su un'istanza di un oggetto si verifica quando un oggetto della classe che si sta tentando di utilizzare non viene istanziato. Per esempio:

Supponi di avere una classe chiamata Student.

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

Ora, considera un'altra lezione in cui stai cercando di recuperare il nome completo dello studente.

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

Come visto nel codice sopra, la dichiarazione Student s - dichiara solo la variabile di tipo Student, nota che la classe Student non è istanziata a questo punto. Quindi, quando viene eseguita l'istruzione s.GetFullName () , genererà NullReferenceException.


3

Bene, in termini semplici:

Stai tentando di accedere a un oggetto che non è stato creato o che al momento non è in memoria.

Quindi, come affrontare questo:

  1. Effettua il debug e lascia che il debugger si rompa ... Ti porterà direttamente alla variabile che è rotta ... Ora il tuo compito è semplicemente quello di risolvere questo problema .. Usando la nuova parola chiave nel posto appropriato.

  2. Se è causato da alcuni comandi del database perché l'oggetto non è presente, è sufficiente fare un controllo nullo e gestirlo:

    if (i == null) {
        // Handle this
    }
  3. Il più difficile .. se il GC ha già raccolto l'oggetto ... Ciò si verifica in genere se si sta cercando di trovare un oggetto usando le stringhe ... Cioè, trovandolo per nome dell'oggetto, può accadere che il GC potrebbe già ripulito ... Questo è difficile da trovare e diventerà un problema ... Un modo migliore per affrontarlo è fare controlli nulli ove necessario durante il processo di sviluppo. Questo ti farà risparmiare un sacco di tempo.

Trovando per nome intendo un po 'di framework che consente a FIndObjects di usare stringhe e il codice potrebbe apparire così: FindObject ("ObjectName");


3
Se hai un riferimento a un oggetto, il GC non lo pulisce mai
John Saunders,

2
se usi cose come FindObject ("Nome dell'oggetto") non c'è modo in cui GC saprà prima che riferirai quell'oggetto .. questo è ciò che sta cercando di spiegare .. questi si verificano in fase di esecuzione
Akash Gutha

2
Esistono alcuni framework che forniscono questa funzionalità in C # come Unity. la domanda non ha nulla a che fare con BCl. Cerca in Internet prima di criticare ci sono un sacco di funzioni come loro e per le tue informazioni gentili le uso persino quotidianamente. Ora, per favore, dimmi come fa la risposta a non avere senso.
Akash Gutha,

2
docs.unity3d.com/ScriptReference/… controlla il link e correggi mr.expert: p
Akash Gutha

Gli esempi che ho visto nel tuo link assegnano i risultati di GameObject.Find a un campo membro. Questo è un riferimento e il GC non lo raccoglierà fino a quando l'oggetto contenente non viene raccolto.
John Saunders,

1

Letteralmente il modo più semplice per riparare un NullReferenceExeption ha due modi. Se hai un GameObject, ad esempio, con uno script allegato e una variabile denominata rb (rigidbody), questa variabile inizierà null quando inizi il gioco.
Questo è il motivo per cui si ottiene NullReferenceExeption perché il computer non ha dati memorizzati in quella variabile.

Userò una variabile RigidBody come esempio.
Possiamo aggiungere dati davvero facilmente in realtà in alcuni modi:

  1. Aggiungi un RigidBody al tuo oggetto con AddComponent> Fisica> Rigidbody
    Quindi vai nello script e digita rb = GetComponent<Rigidbody>();
    Questa riga di codice funziona meglio con le tue Start()o Awake()funzioni.
  2. È possibile aggiungere un componente a livello di codice e assegnare la variabile contemporaneamente con una riga di codice: rb = AddComponent<RigidBody>();

Ulteriori note: se vuoi che l'unità aggiunga un componente al tuo oggetto e potresti aver dimenticato di aggiungerne uno, puoi digitare [RequireComponent(typeof(RigidBody))]sopra la tua dichiarazione di classe (lo spazio sotto tutti i tuoi utilizzi).
Divertiti e divertiti a creare giochi!


-1

Se consideriamo scenari comuni in cui è possibile generare questa eccezione, accedendo alle proprietà con l'oggetto in alto.

Ex:

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

qui, se l'indirizzo è null, otterrai NullReferenceException.

Quindi, come pratica, dovremmo sempre usare il controllo null, prima di accedere alle proprietà di tali oggetti (specialmente in termini generici)

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

-3

Questa è fondamentalmente un'eccezione di riferimento Null . Come afferma Microsoft-

Viene generata un'eccezione NullReferenceException quando si tenta di accedere a un membro di un tipo il cui valore è null.

Cosa significa?

Ciò significa che se un membro che non detiene alcun valore e lo stiamo facendo per eseguire determinate attività, il sistema lancerà senza dubbio un messaggio e dirà:

"Ehi aspetta, quel membro non ha valori, quindi non può eseguire l'attività che lo stai consegnando."

L'eccezione stessa afferma che viene fatto riferimento a qualcosa ma il cui valore non viene impostato. Quindi questo indica che si verifica solo durante l'utilizzo di tipi di riferimento poiché i tipi di valore non sono annullabili.

NullReferenceException non si verificherà se utilizziamo membri del tipo Value.

class Program
{
    static void Main(string[] args)
    {
        string str = null;
        Console.WriteLine(str.Length);
        Console.ReadLine();
    }
}

Il codice sopra mostra una stringa semplice che è assegnata con un null valore .

Ora, quando provo a stampare la lunghezza della stringa str , ottengo un'eccezione non gestita del tipo "System.NullReferenceException" che si è verificato perché il membro str punta a null e non può esserci alcuna lunghezza di null.

' NullReferenceException ' si verifica anche quando dimentichiamo di creare un'istanza di un tipo di riferimento.

Supponiamo di avere una classe e un metodo membro. Non ho istanziato la mia classe, ma ho solo chiamato la mia classe. Ora, se provo ad usare il metodo, il compilatore genererà un errore o emetterà un avviso (a seconda del compilatore).

class Program
{
    static void Main(string[] args)
    {
        MyClass1 obj;
        obj.foo();  //Use of unassigned local variable 'obj'
    }
}

public class MyClass1
{
    internal void foo()
    {
        Console.WriteLine("hello from foo");

    }
}

Il compilatore per il codice sopra riportato genera un errore che la variabile obj non è assegnata, il che significa che la nostra variabile ha valori nulli o nulla. Il compilatore per il codice sopra riportato genera un errore che la variabile obj non è assegnata, il che significa che la nostra variabile ha valori nulli o nulla.

Perché succede?

  • NullReferenceException nasce a causa di un nostro errore per non aver verificato il valore dell'oggetto. Spesso lasciamo deselezionati i valori degli oggetti nello sviluppo del codice.

  • Sorge anche quando ci dimentichiamo di istanziare i nostri oggetti. L'uso di metodi, proprietà, raccolte ecc. Che possono restituire o impostare valori null può anche essere la causa di questa eccezione.

Come può essere evitato?

Esistono vari modi e metodi per evitare questa rinomata eccezione:

  1. Controllo esplicito: dovremmo aderire alla tradizione del controllo di oggetti, proprietà, metodi, matrici e raccolte se sono nulli. Questo può essere semplicemente implementato usando istruzioni condizionali come if-else if-else ecc.

  2. Gestione delle eccezioni: uno dei modi importanti per gestire questa eccezione. Utilizzando semplici blocchi try-catch-finally possiamo controllare questa eccezione e conservarne anche un registro. Questo può essere molto utile quando l'applicazione è in fase di produzione.

  3. Operatori null: l'operatore Null Coalescing e gli operatori null null possono anche essere utili quando si impostano valori su oggetti, variabili, proprietà e campi.

  4. Debugger: per gli sviluppatori, abbiamo la grande arma del debugging con noi. Se abbiamo affrontato NullReferenceException durante la faccia di sviluppo, possiamo usare il debugger per arrivare alla fonte dell'eccezione.

  5. Metodo integrato: metodi di sistema come GetValueOrDefault (), IsNullOrWhiteSpace () e IsNullorEmpty () verifica la presenza di valori null e assegna il valore predefinito se esiste un valore null.

Ci sono già molte buone risposte qui. Puoi anche controllare una descrizione più dettagliata con esempi sul mio blog .

Spero che questo aiuti anche!


Fondamentalmente hai copiato la metà di quel post sul blog e non hai aggiunto nulla di nuovo a cui le risposte esistenti non rispondono.
CodeCaster,

@codecaster Si dice copiando quando si riscrive un riassunto dal proprio blog. So che non c'è nulla di nuovo nella mia risposta e niente di nuovo che le risposte precedenti non hanno, ma desidero contribuire in modo più sofisticato e far capire agli altri come ho capito. Sarà contento anche se aiuta una sola persona. In buona fede.
Wasim,

-4

Se uno riceve questo messaggio durante il salvataggio o la compilazione della build, basta chiudere tutti i file e quindi aprire qualsiasi file per compilare e salvare.

Per me il motivo era che avevo rinominato il file e il vecchio file era ancora aperto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.