Domanda Cos'è una NullReferenceException e come risolverlo?


Ho del codice e quando viene eseguito, getta un NullReferenceException, detto:

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

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


1878


origine


risposte:


Qual è la causa?

Linea di fondo

Stai cercando di usare qualcosa che è null (o Nothing in VB.NET). Questo significa che lo hai impostato nullo non lo hai mai impostato su nulla.

Come qualsiasi altra cosa, null viene passato in giro. Se è null  in metodo "A", potrebbe essere quel metodo "B" passato a null  a metodo "A".

null può avere significati diversi:

  1. Variabili dell'oggetto che sono inizializzata e quindi punto a nulla. In questo caso, se si accede a proprietà o metodi di tali oggetti, provoca a NullReferenceException.
  2. Lo sviluppatore è utilizzando null intenzionalmente indicare che non è disponibile un valore significativo. Nota che C # ha il concetto di tipi di dati nullable per variabili (come le tabelle di database possono avere campi nullable) - puoi assegnare null a loro per indicare che non vi è alcun valore memorizzato in esso, per esempio int? a = null; dove il punto interrogativo indica che è consentito memorizzare null nella variabile a. Puoi verificarlo con if (a.HasValue) {...} o con if (a==null) {...}. Variabili nulle, come a questo esempio, consente di accedere al valore tramite a.Value esplicitamente, o semplicemente normale via a.
    Nota che accedendo via a.Value lancia un InvalidOperationException invece di a NullReferenceException Se a è null - dovresti fare il controllo in anticipo, cioè se hai un'altra variabile on-nullable int b; allora dovresti fare compiti come if (a.HasValue) { b = a.Value; } o più corto if (a != null) { b = a; }.

Il resto di questo articolo va più nel dettaglio e mostra gli errori che molti programmatori spesso fanno quali possono portare a a NullReferenceException.

Più specificamente

Il tempo di esecuzione lanciando a NullReferenceException  sempre significa la stessa cosa: stai provando a usare un riferimento e il riferimento non è inizializzato (o lo era una volta inizializzato, ma lo è Non più inizializzato).

Questo significa che il riferimento è nulle non è possibile accedere ai membri (come i metodi) tramite a null riferimento. Il caso più semplice:

string foo = null;
foo.ToUpper();

Questo lancerà a NullReferenceException alla seconda riga perché non è possibile chiamare il metodo di istanza ToUpper() a string riferimento che punta a null.

Debug

Come trovi la fonte di a NullReferenceException? A parte l'osservazione dell'eccezione stessa, che verrà lanciata esattamente nel punto in cui si verifica, si applicano le regole generali di debugging in Visual Studio: posizionare i breakpoints strategici e ispeziona le tue variabili, passando il mouse sopra i loro nomi, aprendo una finestra (di controllo rapido) o utilizzando i vari pannelli di debug come Locals e Auto.

Se vuoi scoprire dove il riferimento è o non è impostato, fai clic con il tasto destro sul suo nome e seleziona "Trova tutti i riferimenti". È quindi possibile inserire un punto di interruzione in ogni posizione trovata ed eseguire il programma con il debugger allegato. Ogni volta che il debugger si interrompe su 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 ci si aspetta che lo faccia.

Seguendo il flusso del programma in questo modo, è possibile trovare la posizione in cui l'istanza non deve essere nullo 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 è null, allora 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

Nello specifico, in HttpContext.Current.User.Identity.Name, il HttpContext.Current potrebbe essere nullo, o il User la proprietà potrebbe essere nullo o Identity la proprietà 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 principale (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 il new la parola chiave è usata, crea solo una nuova istanza di Book, ma non una nuova istanza di Person, così il Author la proprietà è ancora null.

Inizializzatori di raccolta nidificati

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

Gli inizializzatori della raccolta nidificata si comportano 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" });

Il new Person crea solo un'istanza di Person, ma il Books la raccolta è ancora null. La sintassi di inizializzatore della raccolta non crea una raccolta per p1.Books, si traduce solo in p1.Books.Add(...) dichiarazioni.

schieramento

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

Elementi di matrice

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.

Range Variable (Indirect / Deferred)

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

Convenzioni sui nomi errati:

Se hai nominato campi diversi da quelli locali, potresti aver capito di non aver mai inizializzato il campo.

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

Questo può essere risolto seguendo la convenzione per prefissare i campi con un carattere di sottolineatura:

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 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 di visualizzazione vuota ASP.NET MVC

Se l'eccezione si verifica quando si fa riferimento a una proprietà di @Model in una vista ASP.NET MVC, è necessario comprendere che il Model viene impostato nel metodo di azione, quando tu return una vista. Quando si restituisce un modello vuoto (o una 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

I controlli WPF vengono creati durante la chiamata a InitializeComponent nell'ordine in cui appaiono nell'albero visivo. UN NullReferenceException sarà sollevato nel caso di controlli creati in anticipo con gestori di eventi, ecc InitializeComponent che fa riferimento ai controlli creati in ritardo.

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_SelectionChanged tenta 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!!
}

Modifica dell'ordine delle dichiarazioni in XAML (ad es label1 prima comboBox1, ignorando i problemi della filosofia del design, si risolverebbe almeno il NullReferenceException Qui.

Cast as

var myThing = someObject as Thing;

Questo non genera una InvalidCastException ma restituisce a null quando il cast fallisce (e quando someObject è esso stesso null). Quindi sii consapevole di ciò.

LINQ FirstOrDefault () e SingleOrDefault ()

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

per ciascuno

foreach gira quando si tenta di iterare la raccolta nullo. Solitamente causato da imprevisti null risultato da metodi che restituiscono raccolte.

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

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

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

Modi da evitare

Controlla esplicitamente per null e ignora valori nulli.

Se si prevede che il riferimento a volte sia nullo, è possibile verificarne l'esistenza null prima di accedere ai membri dell'istanza:

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

Controlla esplicitamente per null e fornire un valore predefinito.

I metodi di chiamata che prevedi di restituire possono restituire un'istanza null, ad esempio quando l'oggetto ricercato non può essere trovato. Puoi scegliere di restituire un valore predefinito quando questo è il caso:

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

Controlla esplicitamente per null dalle chiamate di metodo e genera un'eccezione personalizzata.

Puoi anche lanciare un'eccezione personalizzata, solo per prenderla 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;
}

Uso Debug.Assert se un valore non dovrebbe mai essere null, per individuare 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() interrompere il prima 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.
}

Anche se questo controllo non finirà nella tua build di rilascio, provocando il lancio del NullReferenceException di nuovo quando book == null in fase di runtime in modalità di rilascio.

Uso GetValueOrDefault() per i tipi di valori nullable per fornire un valore predefinito quando 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

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

La scorciatoia per fornire un valore predefinito quando a null si incontra:

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 della condizione nulla: ?. o ?[x] per gli array (disponibili in C # 6 e VB.NET 14):

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

var title = person.Title.ToUpper();

Se la persona non ha un titolo, questo genererà un'eccezione perché sta provando a chiamare ToUpper su una proprietà con valore null.

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

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

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

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

Ciò comporterà l'essere della variabile del titolo nulle la chiamata a ToUpper non è fatto se person.Title è null.

Ovviamente tu ancora controllare title per null o utilizzare l'operatore della condizione nulla insieme all'operatore null coalescente (??) 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 che puoi usare ?[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 è nullo, l'espressione restituisce null e puoi tranquillamente controllarlo. Se contiene un array, farà lo stesso di: elem = myIntArray[i]; e restituisce il iesimo elemento.

Tecniche speciali per il debug e il fixing di derefs null negli iteratori

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

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 whatever risultati in null poi MakeFrob getterà. 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 in realtà non lo è correre fino al foreach! La chiamata a GetFrobs restituisce semplicemente un oggetto che quando è iterato eseguirà il blocco iteratore.

Scrivendo un assegno null come questo si impedisce il dereferenziamento nullo, ma si sposta l'eccezione argomento nullo al punto del iterazione, non al punto del 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();
}

Vale a dire, creare un metodo di helper privato con la logica del blocco iteratore e un metodo di superficie pubblica che esegue il controllo Null e restituisce l'iteratore. Ora quando GetFrobs viene chiamato, il controllo nullo avviene immediatamente e quindi GetFrobsForReal viene eseguito quando la sequenza viene iterata.

Se si esamina la fonte di riferimento per LINQ su oggetti, si vedrà che questa tecnica è utilizzata in tutto. È leggermente più rozzo da scrivere, ma rende molto più facile il debug degli errori di nullità. Ottimizza il tuo codice per la comodità del chiamante, non per la comodità dell'autore.

Una nota su null dereferences 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 sicurezza della memoria e 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, dovresti essere a conoscenza di due fatti importanti:

  • dereferenziare un null pointer produce la stessa eccezione del dereferenziamento di un null riferimento
  • dereferenziamento di un puntatore non null non valido può produrre quell'eccezione in alcune circostanze

Per capirne il motivo, è utile capire in che modo .NET genera eccezioni di dereference null in primo luogo. (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 monitorate dal sistema operativo. Ogni pagina di memoria ha dei flag su di essa che determinano come può essere usata: leggere, scrivere, eseguire e così via. Il minore la pagina è contrassegnata come "produce un errore se mai usato in alcun modo".

Sia un puntatore nullo sia un riferimento nullo in C # sono internamente rappresentati come il numero zero, e quindi qualsiasi tentativo di dereferenziarlo nella sua memoria di memoria corrispondente fa sì che il sistema operativo produca un errore. Il runtime .NET rileva quindi questo errore e lo trasforma nell'eccezione di dereferenziazione nulla.

Ecco perché il dereferenziamento sia di un puntatore nullo sia di un riferimento nullo produce la stessa eccezione.

E il secondo punto? dereferenziazione qualunque il puntatore non valido che rientra nella pagina più bassa della memoria virtuale causa lo stesso errore del sistema operativo, e quindi la stessa eccezione.

Perché ha senso? Bene, supponiamo di avere una struttura contenente due interi e un puntatore non gestito uguale a null. Se tentiamo di dereferenziare il secondo int nella struct, il CLR non tenterà di accedere allo storage a zero posizione; accederà allo spazio di archiviazione nella posizione quattro. Ma logicamente questo è un dereferimento nullo perché stiamo arrivando a quell'indirizzo attraverso il null.

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


2105



Eccezione NullReference - Visual Basic

Il NullReference Exception per Visual Basic non è diverso da quello in C #. Dopo tutto, entrambi riportano la stessa eccezione definita in .NET Framework che entrambi utilizzano. 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 domande passate di Stack Overflow. Questo per massimizzare la pertinenza usando il tipi di situazioni spesso viste in post. Un po 'più di spiegazione è prevista anche per coloro che potrebbero averne bisogno. Un esempio simile al tuo è molto probabilmente elencato qui.

Nota:

  1. Questo è basato su concetti: non c'è un codice da incollare nel tuo progetto. Ha lo scopo di aiutarti a capire quali sono le cause a NullReferenceException (NRE), come trovarlo, come risolverlo e come evitarlo. Un NRE può essere causato in molti modi, quindi è improbabile che sia il tuo unico incontro.
  2. Gli esempi (dai post Stack Overflow) non sempre mostrano 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" significa che stai tentando di utilizzare un oggetto che non è stato inizializzato. Questo si riduce a uno di questi:

  • Il tuo codice dichiarato una variabile oggetto, ma non ha inizializzare (crea un'istanza o 'istanziare'esso)
  • Qualcosa che il tuo codice supponeva avrebbe inizializzato un oggetto, no
  • Forse, un altro codice ha invalidato prematuramente un oggetto ancora in uso

Trovare la causa

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

IDE debug display

Dovresti anche rimuovere tutti i blocchi Try / Catch dal codice rilevante, specialmente quelli in cui non c'è nulla nel blocco Catch. Ciò causerà il blocco del codice quando tenta di utilizzare un oggetto che lo è Nothing. Questo è quello che vuoi perché identificherà l'esatto Posizione del problema e consentire di identificare l'oggetto che lo causa.

UN MsgBox nel Catch che mostra Error while... sarà di scarso aiuto. Questo metodo porta anche a molto brutto Stack Overflow domande, perché non è possibile descrivere l'eccezione reale, l'oggetto in questione o anche la linea di codice in cui si verifica.

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

Una volta che sai cosa e dove è il problema, di solito è abbastanza facile da correggere e più veloce di pubblicare una nuova domanda.

Guarda anche:

Esempi e rimedi

Oggetti di classe / Creazione di un'istanza

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

Il problema è che Dim non crea un CashRegister oggetto; dichiara solo una variabile chiamata reg di quel tipo. dichiarare una variabile oggetto e creando un esempio sono due cose diverse.

Rimedio

Il New l'operatore può spesso essere utilizzato per creare l'istanza quando la si dichiara:

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

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

Quando è solo opportuno creare l'istanza in un secondo momento:

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

Nota: Non uso Dim di nuovo in una procedura, incluso il costruttore (Sub New):

Private reg As CashRegister
'...

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

Questo creerà a Locale variabile, reg, che esiste solo in quel contesto (sotto). Il reg variabile con il livello del modulo Scope che userete ovunque rimane il resto Nothing.

Manca il New l'operatore è la causa n. 1 di NullReference Exceptions visto nelle domande di Overflow dello stack esaminate.

Visual Basic tenta di rendere chiaro il processo ripetutamente utilizzando New: Usando il New L'operatore crea a nuovo oggetto e chiama Sub New - il costruttore - dove il tuo oggetto può eseguire qualsiasi altra inizializzazione.

Per essere chiari, Dim (o Private) solo dichiara una variabile e sua Type. Il Scopo della variabile - se esiste per l'intero modulo / classe o è locale per una procedura - è determinata da dove è dichiarato. Private | Friend | Public definisce il livello di accesso, no Scopo.

Per ulteriori informazioni, vedere:


Array

Le matrici devono anche essere istanziate:

Private arr as String()

Questo array è stato 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 utilizzando un letterale e Option Infer, il As <Type> e New gli elementi sono opzionali:

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 che vengono assegnati. Richieste di livello di classe / modulo ancora necessarie 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

La matrice è stata creata, ma il Foogli oggetti in esso non hanno.

Rimedio

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

Usare un 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 collezioni

Le raccolte .NET (di cui ci sono molte varietà - Liste, Dizionario, ecc.) Devono anche essere istanziate o create.

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

Ottieni la stessa eccezione per lo stesso motivo: myList è stato dichiarato 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 determineranno un NRE, perché barList è solo dichiarato, non istanziato. Creare un'istanza di Foo non 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 del fornitore di dati

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

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, il ds L'oggetto dataset è stato dichiarato, ma non è mai stata creata un'istanza. Il DataAdapter riempirà un esistente DataSet, non crearne uno. In questo caso, da ds è una variabile locale, l'IDE ti avverte che questo possa accadere:

img

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

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 è un problema qui: Employees vs Employee. Non c'era DataTable chiamato "Dipendente" creato, quindi a NullReferenceException risultati cercando di accedervi. Un altro potenziale problema è ipotizzando che ci sarà Items che potrebbe non essere così quando l'SQL include una clausola WHERE.

Rimedio

Dal momento che questo utilizza una tabella, utilizzando Tables(0) eviterà errori di ortografia. esaminando Rows.Count può 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 Rows interessato 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 DataAdapter provvederà TableNames come mostrato nell'esempio precedente, ma non analizza i nomi dalla tabella SQL o database. Di conseguenza, ds.Tables("TICKET_RESERVATION") fa riferimento a una tabella inesistente.

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

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

Guarda anche Classe DataTable.


Percorsi oggetto / nidificati

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

Il codice è solo test Items mentre entrambi myFoo e Bar potrebbe anche essere Nulla. Il rimedio è testare l'intera catena o il percorso degli 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 il primo False condizione si incontra. Ciò consente al codice di "perforare" in sicurezza l'oggetto (i) un "livello" alla volta, valutando myFoo.Bar solo dopo (e se) myFoo è determinato ad essere valido. Catene o percorsi di oggetti possono diventare piuttosto lunghi durante la codifica di oggetti complessi:

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

Non è possibile fare riferimento a qualcosa "a valle" di a null oggetto. Questo vale anche per i controlli:

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

Qui, myWebBrowser o Document potrebbe essere Niente o il formfld1 l'elemento 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 non abbia selezionato qualcosa in uno o più controlli dell'interfaccia utente. ListBox1.SelectedItem potrebbe essere Nothing, così ListBox1.SelectedItem.ToString risulterà in un NRE.

Rimedio

Convalidare i dati prima di utilizzarli (utilizzare anche Option Strict e 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...


Form 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 lo segnalerà Controls non esiste nel contesto corrente o "non può fare riferimento a membri non statici". Quindi, in una certa misura, questa è una situazione VB-only. È anche complesso perché può causare una cascata di errori.

Gli array e le raccolte non possono essere inizializzati in questo modo. Questo codice di inizializzazione verrà eseguito prima il costruttore crea il Form o il Controls. Di conseguenza:

  • Gli elenchi e le raccolte saranno semplicemente vuoti
  • L'array conterrà cinque elementi di Nothing
  • Il somevar l'assegnazione comporterà una NRE immediata perché Nulla non ha un .Text proprietà

Il riferimento agli elementi dell'array in seguito comporterà un NRE. Se lo fai dentro Form_Load, a causa di un bug strano, l'IDE non può segnala l'eccezione quando succede. Apparirà l'eccezione dopo quando il codice tenta di utilizzare la matrice. Questa "eccezione silenziosa" è dettagliato in questo post. Per i nostri scopi, la chiave è che quando succede qualcosa di catastrofico durante la creazione di un modulo (Sub New o Form Load evento), le eccezioni possono non essere segnalate, il codice esce dalla procedura e visualizza solo il modulo.

Dal momento che nessun altro codice nel tuo Sub New o Form Load l'evento verrà eseguito dopo la NRE, molte altre cose può essere lasciato non inizializzato.

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

Nota questo vale per tutti i riferimenti di controllo e componenti che rendono questi 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

È curioso che VB non fornisca un avvertimento, ma il rimedio è farlo dichiarare i contenitori a livello di forma, ma inizializzare li in forma carica gestore di eventi quando i controlli fare esistere. Questo può essere fatto in Sub New finché il codice è dopo il InitializeComponent chiamata:

' 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 non può ancora essere fuori dal bosco. Qualsiasi controllo che si trova in un controllo contenitore (come a GroupBox o Panel) non sarà trovato in Me.Controls; saranno nella collezione Controls di quel Panel o GroupBox. Né verrà restituito un controllo quando il nome del controllo è errato ("TeStBox2"). In tali casi, Nothing verrà nuovamente memorizzato in quegli elementi dell'array e verrà generato un NRE quando si tenta di fare riferimento a esso.

Questi dovrebbero essere facili da trovare ora che sai cosa stai cercando: VS shows you the error of your ways

"Button2" risiede su a Panel

Rimedio

Piuttosto che riferimenti indiretti per nome usando il modulo Controlscollezione, usa 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 a NullReferenceException può risultare'. È possibile sopprimere l'avviso, sostituendo Exit Function con Return Nothing, ma questo non risolve il problema. Qualcosa che cerca di usare il ritorno quando someCondition = False risulterà in una NRE:

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

Rimedio

Sostituire Exit Function nella funzione con Return bList. Tornando a vuoto  List non è la stessa cosa di tornare Nothing. Se c'è una possibilità che un oggetto restituito possa essere Nothing, prova prima di usarlo:

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

Prova / catture implementate in modo errato

Una Prova / Catch mal implementata può nascondere dove si trova il problema e generarne 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 è il caso in cui un oggetto non viene creato come previsto, ma dimostra anche l'utilità contatore di un vuoto Catch.

C'è una virgola in più nell'SQL (dopo 'mailaddress') che risulta in un'eccezione a .ExecuteReader. Dopo il Catch non fa nulla, Finally cerca di eseguire la pulizia, ma dal momento che non è possibile Close un null DataReader oggetto, un nuovo di zecca NullReferenceException risultati.

Un vuoto Catch il blocco è il campo da gioco del diavolo. Questo OP era sconcertato perché stava ottenendo un NRE nel Finally bloccare. In altre situazioni, un vuoto Catch potrebbe causare qualcos'altro molto più a valle andare 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 vuoti blocchi Try / Catch: fai in modo che il codice si blocchi in modo da poter a) identificare la causa b) identificare la posizione e c) applicare un rimedio adeguato. I blocchi Try / Catch non hanno lo scopo di nascondere le eccezioni della persona in modo univoco qualificato per risolverli - lo sviluppatore.


DBNull non è uguale a Nothing

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

Il IsDBNull la funzione è usata per verificare se a valore equivale System.DBNull: Da MSDN:

Il valore System.DBNull indica che l'oggetto rappresenta dati mancanti o inesistenti. DBNull non è uguale a Nothing, 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
       ...

FirstOrDefault restituisce il primo elemento o il valore predefinito, che è Nothing per 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 CheckBox con chkName non può essere trovato (o esiste in a GroupBox), poi chk sarà Nothing e tentare di fare riferimento a qualsiasi proprietà comporterà un'eccezione.

Rimedio

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

The DataGridView

Il DGV ha alcune stranezze viste 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 dgvBooks ha AutoGenerateColumns = True, creerà le colonne, ma non le nominerà, quindi il codice sopra riportato fallisce quando fa riferimento ad esse per nome.

Rimedio

Assegna un nome alle colonne manualmente o fai riferimento all'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 ha AllowUserToAddRows come True (il valore predefinito), il Cells nello spazio vuoto / nuova riga in fondo tutti contengono Nothing. La maggior parte dei tentativi di utilizzare i contenuti (ad esempio, ToString) comporterà una NRE.

Rimedio

Usare un For/Each loop e testare il IsNewRow proprietà per determinare se è l'ultima riga. 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 usi a For n loop, modificare il conteggio delle righe o utilizzare Exit For quando IsNewRow è vero.


My.Settings (StringCollection)

In determinate circostanze, provare a utilizzare un oggetto da My.Settings il quale è un StringCollection può risultare in una NullReference la prima volta che la usi. La soluzione è la stessa, ma non così ovvia. Prendere in considerazione:

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

Poiché VB sta gestendo 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 Impostazioni). Poiché la raccolta è (apparentemente) inizializzata quando viene aggiunto un elemento, rimane Nothing quando non ci sono elementi nell'editor Impostazioni da aggiungere.

Rimedio

Inizializza la raccolta delle impostazioni nel modulo Load gestore di eventi, se / quando necessario:

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

In genere, il Settings la raccolta deve essere inizializzata solo la prima volta che viene eseguita l'applicazione. Un rimedio alternativo consiste nell'aggiungere un valore iniziale alla tua raccolta in Progetto -> Impostazioni | FooBars, salva il progetto, quindi rimuovi il valore falso.


Punti chiave

Probabilmente hai dimenticato il New operatore.

o

Qualcosa che hai assunto avrebbe funzionato in modo impeccabile per restituire un oggetto inizializzato al tuo codice, no.

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


MSDN eccezione NullReference


273



Un altro scenario è quando lanci un oggetto nullo in a tipo di valore. Ad esempio, il codice qui sotto:

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

Lancia un NullReferenceException nel cast. Sembra abbastanza ovvio nell'esempio sopra, ma questo può accadere in più intricati scenari "tardivi" in cui l'oggetto nullo è stato restituito da un codice che non possiedi, e il cast è generato ad esempio da qualche sistema automatico.

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

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

Qui, SelectedDate è in effetti una proprietà - di DateTime tipo - del Calendar Tipo di controllo Web e l'associazione potrebbe restituire perfettamente qualcosa di nullo. Il generatore implicito di ASP.NET creerà una parte di codice che sarà equivalente al codice di cast sopra riportato. E questo aumenterà a NullReferenceException questo è abbastanza difficile da individuare, perché si trova nel codice generato da ASP.NET che compila bene ...


217



Significa che la variabile in questione non è puntata sul nulla. Potrei generare questo in questo modo:

SqlConnection connection = null;
connection.Open();

Questo genererà l'errore perché mentre ho dichiarato la variabile "connection", non è puntato su nulla, quando provo a chiamare il membro"Open", non c'è alcun riferimento da risolvere e verrà generato 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 se l'oggetto è nullo, controlla con object == null.

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


146



Significa che il tuo codice ha utilizzato una variabile di riferimento oggetto che è stata impostata su null (cioè non ha fatto riferimento a un'istanza dell'oggetto reale).

Per prevenire l'errore, gli oggetti che potrebbero essere nulli dovrebbero essere testati per null prima di essere usati.

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



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/null per 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 su Nothing/null manualmente, o hai chiamato una funzione che imposta la variabile su Nothing/null per te.


90



Un esempio di questa eccezione che viene generata è: quando si sta tentando di verificare qualcosa, ciò è 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, cioè il codice sopra.

In confronto ad una ArgumentNullException che viene tipicamente lanciata come misura difensiva se un metodo si aspetta che ciò che viene passato ad esso non sia nullo.

Maggiori informazioni sono in C # NullReferenceException e Null Parameter.


76



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

Esempio:

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

Puoi semplicemente evitare questo controllando se la variabile non è nullo:

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

Per comprendere appieno perché viene lanciata una NullReferenceException, è importante conoscere la differenza tra tipi di valore e tipi di riferimento.

Quindi, se hai a che fare con tipi di valore, NullReferenceExceptions can non si verificano. Anche se devi stare attento quando hai a che fare con tipi di riferimento!

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

Tipi di riferimento (questi devono essere controllati):

  • dinamico
  • oggetto
  • stringa

Tipi di valore (puoi semplicemente ignorare questi):

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

72



Un altro caso in cui NullReferenceExceptions può accadere è l'uso (errato) del as 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, Book e Car sono tipi incompatibili; un Car non può essere convertito / cast in a Book. Quando questo cast fallisce, as ritorna null. utilizzando mybook dopo questo causa a NullReferenceException.

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

Se ti aspetti che la conversione del tipo abbia sempre successo (cioè sai quale dovrebbe essere l'oggetto in anticipo), allora dovresti usare un cast:

ComicBook cb = (ComicBook)specificBook;

Se non sei sicuro del tipo, ma lo vuoi provare per usarlo come un tipo specifico, quindi utilizzare as:

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

65



Stai utilizzando l'oggetto che contiene il riferimento al valore nullo. Quindi sta dando un'eccezione nulla. Nell'esempio il valore stringa è nullo e quando si verifica la 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 oggetto non impostato su un'istanza   di un oggetto. a Program.Main ()


59



Simon Mourier ha dato questo esempio:

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

dove un unboxing conversione (cast) a partire dal  object (o da una delle classi System.ValueType o System.Enumo da un tipo di interfaccia) a un tipo di valore (diverso da Nullable<>) in sé dà il NullReferenceException.

Nell'altra direzione, a boxe conversione a partire dal un Nullable<> che ha HasValue uguale a false  a un tipo di riferimento, può dare a null riferimento che poi può portare a 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 il pugilato 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 che il runtime utilizza durante il pugilato Nullable<> le istanze.


48