Qu'est-ce qu'une exception NullReferenceException et comment la corriger?

1875

J'ai du code et quand il s'exécute, il lance un NullReferenceException, disant:

La référence d'objet n'est pas définie à une instance d'un objet.

Qu'est-ce que cela signifie et que puis-je faire pour corriger cette erreur?

John Saunders
la source
L'assistant d'exception dans VS 2017 sera plus utile pour diagnostiquer la cause de cette exception - blogs.msdn.microsoft.com/visualstudio/2016/11/28/… sous Nouvel assistant d'exception .
Zev Spitz
Chers futurs visiteurs, les réponses à cette question s'appliquent également à une ArgumentNullException . Si votre question a été fermée en double de celle-ci et que vous rencontrez une ANE, veuillez suivre les instructions dans les réponses pour déboguer et résoudre votre problème.
@will ANE ne devrait se produire que si une valeur NULL est passée en tant que paramètre. Pouvez-vous donner un exemple si une question de l'ANE s'est terminée en double de celle-ci?
John Saunders
Il est venu sur Meta, mais je devrais aller chercher le lien. Mais en ce qui concerne ce commentaire, un ANE est simplement un NRE mais quelqu'un a ajouté un contrôle préemptif, et vous savez au moins exactement ce qui est nul (le nom de l'argument est fourni), il est donc un peu plus facile à diagnostiquer qu'un NRE direct.

Réponses:

2417

Quelle est la cause?

Bottom Line

Vous essayez d'utiliser quelque chose qui est null(ou Nothingdans VB.NET). Cela signifie que vous le définissez nullou que vous ne le définissez jamais du tout.

Comme toute autre chose, se nullfait passer. S'il se trouve null dans la méthode "A", il se peut que la méthode "B" soit passée null à la méthode "A".

null peut avoir différentes significations:

    1. Variables d'objet qui ne sont pas initialisées et ne pointent donc vers rien. Dans ce cas, si vous accédez aux propriétés ou aux méthodes de ces objets, cela provoque a NullReferenceException.
    1. Le développeur utilise nullintentionnellement pour indiquer qu'aucune valeur significative n'est disponible. Notez que C # a le concept de types de données nullables pour les variables (comme les tables de base de données peuvent avoir des champs nullables) - vous pouvez leur affecter nullpour indiquer qu'il n'y a pas de valeur stockée dedans, par exemple int? a = null;lorsque le point d'interrogation indique qu'il est autorisé à stocker null dans variable a. Vous pouvez vérifier cela avec if (a.HasValue) {...}ou avec if (a==null) {...}. Les variables nulles, comme acet exemple, permettent d'accéder à la valeur via a.Valueexplicitement, ou tout simplement via via a.
      Notez que y accéder via a.Valuelance un InvalidOperationExceptionau lieu d'un NullReferenceExceptionif aestnull- vous devez faire la vérification au préalable, c'est-à-dire que si vous avez une autre variable on-nullable int b;alors vous devez faire des affectations comme if (a.HasValue) { b = a.Value; }ou plus courtes if (a != null) { b = a; }.

Le reste de cet article va plus en détail et montre les erreurs que de nombreux programmeurs font souvent et qui peuvent conduire à un NullReferenceException.

Plus précisement

Le runtimejetant un NullReferenceException toujours signifie la même chose: vous essayez d'utiliser une référence, et la référence n'est pas initialisé (ou il a été une fois initialisé, mais il est plus initialisé).

Cela signifie que la référence est nullet que vous ne pouvez pas accéder aux membres (tels que les méthodes) via une nullréférence. Le cas le plus simple:

string foo = null;
foo.ToUpper();

Cela lancera un NullReferenceExceptionà la deuxième ligne car vous ne pouvez pas appeler la méthode d'instance ToUpper()sur une stringréférence pointant vers null.

Débogage

Comment trouvez-vous la source d'un NullReferenceException? En plus de regarder l'exception elle-même, qui sera levée exactement à l'endroit où elle se produit, les règles générales de débogage dans Visual Studio s'appliquent: placez des points d'arrêt stratégiques et inspectez vos variables , soit en passant la souris sur leurs noms, en ouvrant un ( Rapide) Regardez la fenêtre ou utilisez les divers panneaux de débogage comme les sections locales et les automobiles.

Si vous souhaitez savoir où la référence est ou n'est pas définie, cliquez avec le bouton droit sur son nom et sélectionnez "Rechercher toutes les références". Vous pouvez ensuite placer un point d'arrêt à chaque emplacement trouvé et exécuter votre programme avec le débogueur attaché. Chaque fois que le débogueur s'arrête sur un tel point d'arrêt, vous devez déterminer si vous vous attendez à ce que la référence soit non nulle, inspecter la variable et vérifier qu'elle pointe vers une instance lorsque vous vous y attendez.

En suivant le flux du programme de cette façon, vous pouvez trouver l'emplacement où l'instance ne doit pas être nulle et pourquoi elle n'est pas correctement définie.

Exemples

Quelques scénarios courants où l'exception peut être levée:

Générique

ref1.ref2.ref3.member

Si ref1 ou ref2 ou ref3 est nul, vous obtiendrez a NullReferenceException. Si vous souhaitez résoudre le problème, recherchez celui qui est nul en réécrivant l'expression dans son équivalent le plus simple:

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

Plus précisément, dans HttpContext.Current.User.Identity.Name, le HttpContext.Currentpourrait être nul, ou la Userpropriété pourrait être nulle, ou la Identitypropriété pourrait être nulle.

Indirect

public class Person 
{
    public int Age { get; set; }
}
public class Book 
{
    public Person Author { get; set; }
}
public class Example 
{
    public void Foo() 
    {
        Book b1 = new Book();
        int authorAge = b1.Author.Age; // You never initialized the Author property.
                                       // there is no Person to get an Age from.
    }
}

Si vous souhaitez éviter la référence null enfant (Personne), vous pouvez l'initialiser dans le constructeur de l'objet parent (Livre).

Initialiseurs d'objets imbriqués

La même chose s'applique aux initialiseurs d'objets imbriqués:

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

Cela se traduit par

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

Pendant que le newmot-clé est utilisé, il crée seulement une nouvelle instance de Book, mais pas une nouvelle instance de Person, donc Authorla propriété est toujours null.

Initialiseurs de collections imbriquées

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

La collection imbriquée Initializersse comporte de la même manière:

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

Cela se traduit par

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

La new Personseule crée une instance de Person, mais la Bookscollection est toujours null. La Initializersyntaxe de collection ne crée pas de collection pour p1.Books, elle se traduit uniquement par les p1.Books.Add(...)instructions.

Array

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

Éléments du tableau

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.

Tableaux dentelés

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.

Collection / Liste / Dictionnaire

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

Variable de plage (indirecte / différée)

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.

Événements

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. 

classe publique Form1 {client Client privé;

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

}

Cela peut être résolu en suivant la convention pour préfixer les champs avec un trait de soulignement:

    private Customer _customer;

Cycle de vie de la page 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!";
    }
}

Valeurs de session ASP.NET

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

Modèles de vues vides ASP.NET MVC

Si l'exception se produit lors du référencement d'une propriété de @Modeldans un ASP.NET MVC View, vous devez comprendre que le Modelget est défini dans votre méthode d'action, lorsque vous returnune vue. Lorsque vous renvoyez un modèle vide (ou une propriété de modèle) à partir de votre contrôleur, l'exception se produit lorsque les vues y accèdent:

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

Ordre et événements de création de contrôle WPF

WPFles contrôles sont créés lors de l'appel à InitializeComponentdans l'ordre dans lequel ils apparaissent dans l'arborescence visuelle. Un NullReferenceExceptionsera soulevé dans le cas de contrôles créés au début avec des gestionnaires d'événements, etc., qui se déclenchent pendant InitializeComponentlesquels référencent les contrôles créés tardivement.

Par exemple :

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

Ici comboBox1est créé avant label1. Si comboBox1_SelectionChangedvous essayez de référencer `label1, il n'aura pas encore été créé.

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

Changer l'ordre des déclarations dans le XAML(c.-à-d., Énumérer label1avant comboBox1, ignorer les problèmes de philosophie de conception, résoudrait au moins l' NullReferenceExceptionici.

Cast avec as

var myThing = someObject as Thing;

Cela ne lance pas un InvalidCastExceptionmais renvoie un nulllorsque le transtypage échoue (et quand someObjectest lui-même nul). Soyez donc conscient de cela.

LINQ FirstOrDefault()etSingleOrDefault()

Les versions simples First()et Single()lèvent des exceptions quand il n'y a rien. Les versions "OrDefault" renvoient null dans ce cas. Soyez donc conscient de cela.

pour chaque

foreachjette lorsque vous essayez d'itérer la collection null. Habituellement provoqué par un nullrésultat inattendu des méthodes qui retournent des collections.

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

Exemple plus réaliste - sélectionnez des nœuds dans un document XML. Jettera si les nœuds ne sont pas trouvés mais le débogage initial montre que toutes les propriétés valides:

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

Façons à éviter

Vérifiez nullet ignorez explicitement les valeurs nulles.

Si vous vous attendez à ce que la référence soit parfois nulle, vous pouvez vérifier qu'elle est nullavant d'accéder aux membres de l'instance:

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

Recherchez nullet fournissez explicitement une valeur par défaut.

Les méthodes que vous prévoyez de renvoyer peuvent renvoyer une instance null, par exemple lorsque l'objet recherché est introuvable. Vous pouvez choisir de renvoyer une valeur par défaut lorsque c'est le cas:

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

Recherchez explicitement les nullappels de méthode et lancez une exception personnalisée.

Vous pouvez également lever une exception personnalisée, uniquement pour l'attraper dans le code appelant:

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

À utiliser Debug.Assertsi une valeur ne doit jamais l'être null, pour détecter le problème avant que l'exception ne se produise.

Lorsque vous savez pendant le développement qu'une méthode peut peut-être, mais ne devrait jamais revenir null, vous pouvez utiliser Debug.Assert()pour interrompre dès que possible quand elle se produit:

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

Bien que cette vérification ne se termine pas dans votre version , ce qui la fait lancer à NullReferenceExceptionnouveau lors book == nullde l'exécution en mode de publication.

Utilisez GetValueOrDefault()pour les nullabletypes de valeur pour fournir une valeur par défaut lorsqu'ils le sont 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

Utilisez l'opérateur de coalescence nul: ??[C #] ou If()[VB].

Le raccourci pour fournir une valeur par défaut quand un nullest rencontré:

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

Utilisez l'opérateur de condition nulle: ?.ou ?[x]pour les tableaux (disponibles en C # 6 et VB.NET 14):

On l'appelle aussi parfois l'opérateur de navigation sécurisée ou Elvis (d'après sa forme). Si l'expression sur le côté gauche de l'opérateur est nulle, le côté droit ne sera pas évalué et null sera renvoyé à la place. Cela signifie des cas comme celui-ci:

var title = person.Title.ToUpper();

Si la personne n'a pas de titre, cela lèvera une exception car elle essaie d'appeler ToUpperune propriété avec une valeur nulle.

Dans C# 5et en dessous, cela peut être gardé avec:

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

Maintenant, la variable de titre sera nulle au lieu de lever une exception. C # 6 introduit une syntaxe plus courte pour cela:

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

Cela se traduira par la variable de titre étant null, et l'appel à ToUppern'est pas effectué si person.Titleest null.

Bien sûr, vous devez toujours vérifier titlenull ou utiliser l'opérateur de condition null avec l'opérateur de coalescence null ( ??) pour fournir une valeur par défaut:

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

De même, pour les tableaux, vous pouvez utiliser ?[i]comme suit:

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

Cela fera ce qui suit: Si myIntArrayest null, l'expression renvoie null et vous pouvez le vérifier en toute sécurité. S'il contient un tableau, il fera la même chose que: elem = myIntArray[i];et renvoie l' i<sup>th</sup>élément.

Utilisez un contexte nul (disponible en C # 8):

Introduits dans C# 8ce contexte, les types de contexte nullable et de référence nullable effectuent une analyse statique sur les variables et fournissent un avertissement du compilateur si une valeur peut être potentiellement nulle ou avoir été définie sur null. Les types de référence nullable permettent aux types d'être explicitement autorisés à être null.

Le contexte d'annotation nullable et le contexte d'avertissement nullable peuvent être définis pour un projet à l'aide de l' Nullableélément dans votre csprojfichier. Cet élément configure la façon dont le compilateur interprète la nullité des types et quels avertissements sont générés. Les paramètres valides sont:

  • enable: le contexte d'annotation annulable est activé. Le contexte d'avertissement nullable est activé. Les variables d'un type de référence, chaîne par exemple, ne sont pas nullables. Tous les avertissements de nullité sont activés.
  • disable: le contexte d'annotation annulable est désactivé. Le contexte d'avertissement nullable est désactivé. Les variables d'un type de référence sont inconscientes, tout comme les versions antérieures de C #. Tous les avertissements de nullité sont désactivés.
  • safeonly: le contexte d'annotation nullable est activé. Le contexte d'avertissement nullable est sécurisé uniquement. Les variables d'un type de référence sont non nulles. Tous les avertissements de nullité de sécurité sont activés.
  • avertissements: le contexte d'annotation annulable est désactivé. Le contexte d'avertissement nullable est activé. Les variables d'un type de référence sont inconscientes. Tous les avertissements de nullité sont activés.
  • safeonlywarnings: le contexte d'annotation nullable est désactivé. Le contexte d'avertissement nullable est sécurisé uniquement. Les variables d'un type de référence sont inconscientes. Tous les avertissements de nullité de sécurité sont activés.

Un type de référence nullable est noté en utilisant la même syntaxe que les types de valeur nullable: a ?est ajouté au type de la variable.

Techniques spéciales pour déboguer et corriger les derefs nuls dans les itérateurs

C#prend en charge les "blocs d'itérateur" (appelés "générateurs" dans d'autres langues populaires). Les exceptions de déréférencement nul peuvent être particulièrement difficiles à déboguer dans les blocs d'itérateur en raison de l'exécution différée:

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

Si les whateverrésultats en nullpuis MakeFrobjettera. Maintenant, vous pourriez penser que la bonne chose à faire est la suivante:

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

Pourquoi est-ce mal? Parce que le bloc itérateur ne s'exécute pas tant que le foreach! L'appel à GetFrobsrenvoie simplement un objet qui, une fois itéré , exécutera le bloc itérateur.

En écrivant une vérification nulle comme celle-ci, vous empêchez la déréférence nulle, mais vous déplacez l'exception d'argument nul au point de l' itération , pas au point de l' appel , ce qui est très déroutant pour le débogage .

La solution correcte est:

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

Autrement dit, créez une méthode d'assistance privée qui a la logique de blocage de l'itérateur et une méthode de surface publique qui effectue la vérification nulle et renvoie l'itérateur. Maintenant, quand GetFrobsest appelé, la vérification nulle se produit immédiatement, puis GetFrobsForReals'exécute lorsque la séquence est itérée.

Si vous examinez la source de référence pour LINQto Objects, vous verrez que cette technique est utilisée partout. Il est légèrement plus maladroit à écrire, mais il facilite beaucoup le débogage des erreurs de nullité. Optimisez votre code pour la commodité de l'appelant et non pour la commodité de l'auteur .

Une note sur les déréférences nulles dans le code dangereux

C#a un mode "dangereux" qui est, comme son nom l'indique, extrêmement dangereux car les mécanismes de sécurité normaux qui assurent la sécurité de la mémoire et la sécurité du type ne sont pas appliqués. Vous ne devez pas écrire de code dangereux à moins d'avoir une compréhension approfondie et approfondie du fonctionnement de la mémoire .

En mode non sécurisé, vous devez être conscient de deux faits importants:

  • déréférencer un pointeur nul produit la même exception que déréférencer une référence nulle
  • déréférencer un pointeur non nul non valide peut produire cette exception dans certaines circonstances

Pour comprendre pourquoi, il est utile de comprendre comment .NET produit en premier lieu des exceptions de déréférencement nul. (Ces détails s'appliquent à .NET exécuté sur Windows; d'autres systèmes d'exploitation utilisent des mécanismes similaires.)

La mémoire est virtualisée dans Windows; chaque processus obtient un espace de mémoire virtuelle de nombreuses "pages" de mémoire qui sont suivies par le système d'exploitation. Chaque page de mémoire comporte des indicateurs qui déterminent comment elle peut être utilisée: lecture, écriture, exécution, etc. La page la plus basse est marquée comme "produire une erreur si jamais utilisée de quelque façon que ce soit".

Un pointeur nul et une référence nulle dans C#sont représentés en interne sous la forme du nombre zéro, et donc toute tentative de déréférencement dans son stockage mémoire correspondant entraîne une erreur du système d'exploitation. Le runtime .NET détecte ensuite cette erreur et la transforme en exception de déréférence nulle.

C'est pourquoi le déréférencement d'un pointeur nul et d'une référence nulle produit la même exception.

Et le deuxième point? Le déréférencement de tout pointeur non valide qui tombe dans la page la plus basse de la mémoire virtuelle provoque la même erreur du système d'exploitation, et donc la même exception.

Pourquoi est-ce logique? Supposons que nous ayons une structure contenant deux entrées et un pointeur non géré égal à null. Si nous essayons de déréférencer le deuxième entier dans la structure, le CLRne tentera pas d'accéder au stockage à l'emplacement zéro; il accédera au stockage à l'emplacement quatre. Mais logiquement, c'est une déréférence nulle parce que nous arrivons à cette adresse via la valeur nulle.

Si vous travaillez avec du code dangereux et que vous obtenez une exception de déréférence nulle, sachez simplement que le pointeur incriminé n'a pas besoin d'être nul. Il peut s'agir de n'importe quel emplacement dans la page la plus basse et cette exception sera produite.

LopDev
la source
55
Peut-être que c'est un commentaire stupide, mais la première et meilleure façon d'éviter ce problème ne serait-elle pas d'initialiser l'objet? Pour moi, si cette erreur se produit, c'est généralement parce que j'ai oublié d'initialiser quelque chose comme l'élément de tableau. Je pense qu'il est beaucoup moins courant de définir l'objet comme nul et de le référencer ensuite. Peut-être donner le moyen de résoudre chaque problème adjacent à la description. Encore un bon post.
JPK
30
Et s'il n'y a pas d'objet, mais plutôt la valeur de retour d'une méthode ou d'une propriété?
John Saunders
6
L'exemple livre / auteur est un peu bizarre .... Comment ça compile même? Comment fonctionne Intellisense même? Qu'est-ce que je ne suis pas bon avec computar ...
5
@ Will: ma dernière édition aide-t-elle? Sinon, veuillez être plus explicite sur ce que vous voyez comme un problème.
John Saunders, le
6
@JohnSaunders Oh, non, désolé, je voulais dire la version initialiseur d'objet de cela. new Book { Author = { Age = 45 } };Comment l'initialisation interne fonctionne-t-elle même ... Je ne peux pas penser à une situation où l'init interne fonctionnerait jamais, mais il compile et l'intellisense fonctionne ... Sauf pour les structures?
311

Exception NullReference - Visual Basic

Le NullReference Exceptionpour Visual Basic n'est pas différent de celui en C # . Après tout, ils signalent tous deux la même exception définie dans le .NET Framework qu'ils utilisent tous les deux. Les causes propres à Visual Basic sont rares (peut-être une seule).

Cette réponse utilisera les termes, la syntaxe et le contexte Visual Basic. Les exemples utilisés proviennent d'un grand nombre de questions sur le débordement de pile passées. Il s'agit de maximiser la pertinence en utilisant les types de situations souvent vues dans les publications. Un peu plus d'explications sont également fournies pour ceux qui pourraient en avoir besoin. Un exemple similaire au vôtre est très probablement répertorié ici.

Remarque:

  1. C'est basé sur le concept: il n'y a pas de code à coller dans votre projet. Il est destiné à vous aider à comprendre ce qui cause un NullReferenceException(NRE), comment le trouver, comment le réparer et comment l'éviter. Un NRE peut être causé de plusieurs façons, il est donc peu probable que ce soit votre seule rencontre.
  2. Les exemples (tirés des publications Stack Overflow) ne montrent pas toujours la meilleure façon de faire quelque chose en premier lieu.
  3. En règle générale, le remède le plus simple est utilisé.

Signification de base

Le message "Objet non défini sur une instance d'Objet" signifie que vous essayez d'utiliser un objet qui n'a pas été initialisé. Cela se résume à l'un d'eux:

  • Votre code a déclaré une variable objet, mais il ne l'a pas initialisée (créez une instance ou « instanciez-la »)
  • Quelque chose que votre code supposait initialiserait un objet, n'a pas
  • Peut-être, un autre code a invalidé prématurément un objet encore utilisé

Trouver la cause

Puisque le problème est une référence d'objet qui est Nothing, la réponse est de les examiner pour trouver lequel. Déterminez ensuite pourquoi il n'est pas initialisé. Maintenez la souris sur les différentes variables et Visual Studio (VS) affichera leurs valeurs - le coupable sera Nothing.

Affichage du débogage IDE

Vous devez également supprimer tous les blocs Try / Catch du code pertinent, en particulier ceux où il n'y a rien dans le bloc Catch. Cela provoquera le plantage de votre code lorsqu'il essaie d'utiliser un objet qui l'est Nothing. C'est ce que vous voulez car il identifiera l' emplacement exact du problème et vous permettra d'identifier l'objet à l'origine de ce problème.

Un MsgBoxin the Catch qui s'affiche Error while...ne sera d'aucune utilité. Cette méthode conduit également à de très mauvaises questions de débordement de pile, car vous ne pouvez pas décrire l'exception réelle, l'objet impliqué ou même la ligne de code où elle se produit.

Vous pouvez également utiliser le Locals Window( Débogage -> Windows -> Locaux ) pour examiner vos objets.

Une fois que vous savez quel est et où est le problème, il est généralement assez facile à résoudre et plus rapide que de poster une nouvelle question.

Voir également:

Exemples et remèdes

Objets de classe / Création d'une instance

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

Le problème est qu'il Dimne crée pas d' objet CashRegister ; il déclare uniquement une variable nommée regde ce type. La déclaration d' une variable d'objet et la création d'une instance sont deux choses différentes.

Remède

L' Newopérateur peut souvent être utilisé pour créer l'instance lorsque vous la déclarez:

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

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

Lorsqu'il ne convient que de créer l'instance ultérieurement:

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

Remarque: Ne réutilisez Dimpas dans une procédure, y compris le constructeur ( Sub New):

Private reg As CashRegister
'...

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

Cela va créer une variable localereg , qui n'existe que dans ce contexte (sub). La regvariable de niveau module Scopeque vous utiliserez partout ailleurs reste Nothing.

L' Newopérateur manquant est la cause n ° 1 desNullReference Exceptions problèmes de débordement de pile examinés.

Visual Basic essaie de rendre le processus clair à plusieurs reprises en utilisant New: L'utilisation de l' Newopérateur crée un nouvel objet et appelle Sub New- le constructeur - où votre objet peut effectuer toute autre initialisation.

Pour être clair, Dim(ou Private) ne déclare qu'une variable et son Type. L' étendue de la variable - qu'elle existe pour l'ensemble du module / de la classe ou qu'elle soit locale à une procédure - est déterminée par l' endroit où elle est déclarée. Private | Friend | Publicdéfinit le niveau d'accès, pas la portée .

Pour plus d'informations, voir:


Tableaux

Les tableaux doivent également être instanciés:

Private arr as String()

Ce tableau a seulement été déclaré, pas créé. Il existe plusieurs façons d'initialiser un tableau:

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

Remarque: à partir de VS 2010, lors de l'initialisation d'un tableau local à l'aide d'un littéral et Option Infer, les éléments As <Type>et Newsont facultatifs:

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}

Le type de données et la taille du tableau sont déduits des données attribuées. Les déclarations de niveau de classe / module nécessitent toujours As <Type>avec Option Strict:

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

Exemple: tableau d'objets de classe

Dim arrFoo(5) As Foo

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

Le tableau a été créé, mais pas les Fooobjets qu'il contient .

Remède

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

Utiliser un List(Of T)rendra assez difficile d'avoir un élément sans objet valide:

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

Pour plus d'informations, voir:


Listes et collections

Les collections .NET (dont il existe de nombreuses variétés - listes, dictionnaire, etc.) doivent également être instanciées ou créées.

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

Vous obtenez la même exception pour la même raison - myListn'a été déclaré, mais aucune instance n'a été créée. Le remède est le même:

myList = New List(Of String)

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

Un oubli commun est une classe qui utilise une collection 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

L'une ou l'autre procédure se traduira par un NRE, car il barListest uniquement déclaré, et non instancié. La création d'une instance de Foone créera pas également une instance de l'interne barList. C'était peut-être l'intention de le faire dans le constructeur:

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

Comme précédemment, c'est incorrect:

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

Pour plus d'informations, voir List(Of T)Classe .


Objets du fournisseur de données

Travailler avec des bases de données présente de nombreuses possibilités pour une NullReference car il peut y avoir de nombreux objets ( Command, Connection, Transaction, Dataset, DataTable, DataRows....) en usage à la fois. Remarque: Peu importe le fournisseur de données que vous utilisez - MySQL, SQL Server, OleDB, etc. - les concepts sont les mêmes.

Exemple 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

Comme précédemment, l' dsobjet Dataset a été déclaré, mais aucune instance n'a été créée. Le DataAdapterremplira un existant DataSet, pas en créer un. Dans ce cas, puisqu'il dss'agit d'une variable locale, l'EDI vous avertit que cela peut se produire:

img

Lorsqu'il est déclaré en tant que variable de niveau module / classe, comme cela semble être le cas con, le compilateur ne peut pas savoir si l'objet a été créé par une procédure en amont. N'ignorez pas les avertissements.

Remède

Dim ds As New DataSet

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

Une faute de frappe est un problème ici: Employeesvs Employee. Aucun DataTable"employé" n'a été créé, donc un NullReferenceExceptionrésultat a tenté d'y accéder. Un autre problème potentiel est de supposer qu'il y en aura, ce Itemsqui peut ne pas être le cas lorsque le SQL inclut une clause WHERE.

Remède

Comme cela utilise une seule table, l'utilisation Tables(0)évitera les fautes d'orthographe. L'examen Rows.Countpeut également aider:

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

Fillest une fonction renvoyant le nombre de personnes Rowsaffectées qui peut également être testée:

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

Exemple 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

Le DataAdapterfournira TableNamescomme indiqué dans l'exemple précédent, mais il n'analyse pas les noms de la table SQL ou de la base de données. Par conséquent, ds.Tables("TICKET_RESERVATION")référence une table inexistante.

Le remède est le même, référencez le tableau par index:

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

Voir aussi Classe DataTable .


Chemins d'objet / imbriqués

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

Le code ne teste Itemsque les deux myFooet Barpeut également être Nothing. Le remède consiste à tester la chaîne ou le chemin complet des objets un par un:

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

AndAlsoest important. Les tests suivants ne seront pas effectués une fois la première Falsecondition rencontrée. Cela permet au code de «percer» en toute sécurité dans le (s) objet (s) un «niveau» à la fois, en évaluant myFoo.Baruniquement après (et si) myFooest déterminé comme étant valide. Les chaînes ou chemins d'objets peuvent devenir assez longs lors du codage d'objets complexes:

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

Il n'est pas possible de référencer quoi que ce soit «en aval» d'un nullobjet. Cela s'applique également aux contrôles:

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

Ici, myWebBrowserou Documentpourrait être Nothing ou l' formfld1élément peut ne pas exister.


Contrôles de l'interface utilisateur

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)

Entre autres choses, ce code ne prévoit pas que l'utilisateur n'a peut-être pas sélectionné quelque chose dans un ou plusieurs contrôles d'interface utilisateur. ListBox1.SelectedItempourrait bien être Nothing, il ListBox1.SelectedItem.ToStringen résultera un NRE.

Remède

Validez les données avant de les utiliser (utilisez également Option Strictles paramètres SQL et 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

Vous pouvez également utiliser (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Formulaires 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

Il s'agit d'un moyen assez courant d'obtenir un NRE. En C #, selon la façon dont il est codé, l'EDI signalera qu'il Controlsn'existe pas dans le contexte actuel ou "ne peut pas référencer un membre non statique". Donc, dans une certaine mesure, c'est une situation uniquement VB. Il est également complexe car il peut entraîner une cascade de pannes.

Les tableaux et les collections ne peuvent pas être initialisés de cette façon. Ce code d'initialisation s'exécutera avant que le constructeur ne crée le Formou le Controls. Par conséquent:

  • Les listes et la collection seront simplement vides
  • Le tableau contiendra cinq éléments de Nothing
  • L' somevaraffectation entraînera un NRE immédiat car rien n'a de .Textpropriété

Référencer les éléments du tableau ultérieurement entraînera un NRE. Si vous le faites dans Form_Load, en raison d'un bogue étrange, l'EDI peut ne pas signaler l'exception lorsqu'elle se produit. L'exception apparaîtra plus tard lorsque votre code essaie d'utiliser le tableau. Cette "exception silencieuse" est détaillée dans cet article . Pour nos besoins, la clé est que lorsque quelque chose de catastrophique se produit lors de la création d'un formulaire ( Sub Newou d'un Form Loadévénement), les exceptions peuvent ne pas être signalées, le code quitte la procédure et affiche simplement le formulaire.

Puisqu'aucun autre code dans votre événement Sub Newou Form Loadne s'exécutera après le NRE, de nombreuses autres choses peuvent être laissées non initialisées.

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

Notez que cela s'applique à toutes les références de contrôle et de composant qui les rendent illégales où elles se trouvent:

Public Class Form1

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

Remède partiel

Il est curieux que VB ne fournit pas un avertissement, mais le remède est de déclarer les conteneurs au niveau de la forme, mais initialiser les dans le gestionnaire d'événement de chargement de formulaire lorsque les contrôles font exist. Cela peut être fait Sub Newtant que votre code est après l' InitializeComponentappel:

' 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

Le code du tableau n'est peut-être pas encore sorti du bois. Tous les contrôles qui sont dans un contrôle conteneur (comme un GroupBoxou Panel) ne seront pas trouvés dans Me.Controls; ils seront dans la collection Controls de ce Panel ou GroupBox. Un contrôle ne sera pas non plus renvoyé lorsque le nom du contrôle est mal orthographié ( "TeStBox2"). Dans de tels cas, Nothingsera à nouveau stocké dans ces éléments du tableau et un NRE sera généré lorsque vous tenterez de le référencer.

Ceux-ci devraient être faciles à trouver maintenant que vous savez ce que vous recherchez: VS vous montre l'erreur de vos voies

"Button2" réside sur un Panel

Remède

Plutôt que des références indirectes par nom à l'aide de la Controlscollection du formulaire , utilisez la référence de contrôle:

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

Fonction ne retournant rien

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

Il s'agit d'un cas où l'EDI vous avertira que « tous les chemins ne renvoient pas une valeur et NullReferenceExceptionpeuvent en résulter ». Vous pouvez supprimer l'avertissement, en le remplaçant Exit Functionpar Return Nothing, mais cela ne résout pas le problème. Tout ce qui tente d'utiliser le retour someCondition = Falseentraînera un NRE:

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

Remède

Remplacez Exit Functionla fonction par Return bList. Retourner un vide List n'est pas la même chose que retourner Nothing. S'il y a une chance qu'un objet retourné puisse l'être Nothing, testez avant de l'utiliser:

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

Try / Catch mal implémenté

Un Try / Catch mal implémenté peut cacher où se trouve le problème et en entraîner de nouveaux:

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

Il s'agit d'un cas où un objet n'est pas créé comme prévu, mais démontre également la contre-utilité d'un vide Catch.

Il y a une virgule supplémentaire dans le SQL (après 'mailaddress') qui entraîne une exception à .ExecuteReader. Après que le Catchne fait rien, Finallyessaie d'effectuer un nettoyage, mais comme vous ne pouvez pas Closeun DataReaderobjet nul , un tout nouveau NullReferenceExceptionrésultat.

Un Catchbloc vide est le terrain de jeu du diable. Cet OP était perplexe de savoir pourquoi il obtenait un NRE dans le Finallybloc. Dans d'autres situations, un vide Catchpeut entraîner quelque chose d'autre beaucoup plus en aval en se détraquant et vous faire passer du temps à regarder les mauvaises choses au mauvais endroit pour le problème. (L '"exception silencieuse" décrite ci-dessus offre la même valeur de divertissement.)

Remède

N'utilisez pas de blocs Try / Catch vides - laissez le code se bloquer afin que vous puissiez a) identifier la cause b) identifier l'emplacement et c) appliquer un remède approprié. Les blocs Try / Catch ne sont pas destinés à masquer les exceptions à la personne uniquement qualifiée pour les corriger - le développeur.


DBNull n'est pas la même chose que Nothing

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

La IsDBNullfonction est utilisée pour tester si une valeur est égale à System.DBNull: De MSDN:

La valeur System.DBNull indique que l'objet représente des données manquantes ou inexistantes. DBNull n'est pas identique à Nothing, ce qui indique qu'une variable n'a pas encore été initialisée.

Remède

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

Comme précédemment, vous pouvez tester Rien, puis une valeur spécifique:

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

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

FirstOrDefaultrenvoie le premier élément ou la valeur par défaut, qui est Nothingpour les types de référence et jamais DBNull:

If getFoo IsNot Nothing Then...

Les contrôles

Dim chk As CheckBox

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

Si un CheckBoxavec chkNameest introuvable (ou existe dans un GroupBox), alors chksera Nothing et toute tentative de référence à une propriété entraînera une exception.

Remède

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

Le DataGridView

La DGV a quelques caprices vues périodiquement:

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"

Si dgvBooksa AutoGenerateColumns = True, il va créer les colonnes, mais il ne les nomme pas, de sorte que le code ci - dessus échoue lorsqu'il y fait référence par son nom.

Remède

Nommez les colonnes manuellement ou référencez par index:

dgvBooks.Columns(0).Visible = True

Exemple 2 - Méfiez-vous du 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

Lorsque votre DataGridViewa AllowUserToAddRowscomme Truevaleur par défaut, le Cellsdans la ligne vierge / nouvelle en bas contiendra tous Nothing. La plupart des tentatives d'utilisation du contenu (par exemple ToString) entraîneront un NRE.

Remède

Utilisez une For/Eachboucle et testez la IsNewRowpropriété pour déterminer s'il s'agit de cette dernière ligne. Cela fonctionne, que ce AllowUserToAddRowssoit vrai ou non:

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

Si vous utilisez une For nboucle, modifier le nombre de lignes ou de l' utilisation Exit Forquand IsNewRowest vrai.


My.Settings (StringCollection)

Dans certaines circonstances, essayer d'utiliser un élément My.Settingsdont est un StringCollectionpeut entraîner une NullReference la première fois que vous l'utilisez. La solution est la même, mais pas aussi évidente. Considérer:

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

Étant donné que VB gère les paramètres pour vous, il est raisonnable de s'attendre à ce qu'il initialise la collection. Il le sera, mais uniquement si vous avez précédemment ajouté une entrée initiale à la collection (dans l'éditeur de paramètres). Étant donné que la collection est (apparemment) initialisée lorsqu'un élément est ajouté, elle reste Nothinglorsqu'il n'y a aucun élément dans l'éditeur de paramètres à ajouter.

Remède

Initialisez la collection de paramètres dans le Loadgestionnaire d'événements du formulaire , si / quand nécessaire:

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

En règle générale, la Settingscollection ne devra être initialisée que lors de la première exécution de l'application. Un autre remède consiste à ajouter une valeur initiale à votre collection dans Projet -> Paramètres | FooBars , enregistrez le projet, puis supprimez la fausse valeur.


Points clés

Vous avez probablement oublié l' Newopérateur.

ou

Quelque chose que vous supposiez fonctionnerait parfaitement pour retourner un objet initialisé à votre code, ne l'a pas fait.

N'ignorez (jamais) les avertissements du compilateur et utilisez Option Strict On(toujours).


Exception MSDN NullReference

Plutonix
la source
226

Un autre scénario consiste à convertir un objet nul en un type de valeur . Par exemple, le code ci-dessous:

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

Il jettera un NullReferenceExceptionsur le casting. Cela semble assez évident dans l'exemple ci-dessus, mais cela peut se produire dans des scénarios complexes plus "à liaison tardive" où l'objet nul a été renvoyé à partir d'un code que vous ne possédez pas, et le transtypage est par exemple généré par un système automatique.

Un exemple de ceci est ce simple fragment de liaison ASP.NET avec le contrôle Calendar:

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

Ici, il SelectedDates'agit en fait d'une propriété - de DateTimetype - du Calendartype Contrôle Web, et la liaison pourrait parfaitement renvoyer quelque chose de nul. Le générateur implicite ASP.NET créera un morceau de code qui sera équivalent au code cast ci-dessus. Et cela soulèvera un NullReferenceExceptionproblème assez difficile à repérer, car il se trouve dans du code généré par ASP.NET qui compile très bien ...

Simon Mourier
la source
7
Superbe capture. Voie à sens unique pour éviter:DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Serge Shultz
160

Cela signifie que la variable en question ne pointe vers rien. Je pourrais générer ceci comme ceci:

SqlConnection connection = null;
connection.Open();

Cela lèvera l'erreur parce que même si j'ai déclaré la variable " connection", elle n'est pointée vers rien. Lorsque j'essaie d'appeler le membre " Open", il n'y a aucune référence à résoudre et cela générera l'erreur.

Pour éviter cette erreur:

  1. Initialisez toujours vos objets avant d'essayer de faire quoi que ce soit avec eux.
  2. Si vous n'êtes pas sûr que l'objet soit nul, vérifiez-le avec object == null.

L'outil Resharper de JetBrains identifiera chaque endroit dans votre code qui a la possibilité d'une erreur de référence nulle, vous permettant de mettre une vérification nulle. Cette erreur est la première source de bogues, à mon humble avis.

Chris B. Behrens
la source
3
L'outil Resharper de JetBrains identifiera chaque endroit dans votre code qui a la possibilité d'une erreur de référence nulle. Ceci est une erreur. J'ai une solution sans cette détection, mais le code résulte parfois à l'exception. Je soupçonne qu'il est parfois indétectable - du moins par eux - lorsque le multithreading est impliqué, mais je ne peux pas commenter davantage car je n'ai pas encore identifié l'emplacement de mon bogue.
j riv
Mais comment le résoudre lorsque l'exception NullReferenceException arrive en utilisant HttpContext.Current.Responce.Clear (). Il n'est résolu par aucune des solutions ci-dessus. car lors de la création de son objet objet de HttpContext, une erreur survient "La résolution de surcharge a échoué car aucun" Nouveau "accessible n'accepte ce nombre d'arguments.
Sunny Sandeep
158

Cela signifie que votre code a utilisé une variable de référence d'objet définie sur null (c'est-à-dire qu'il ne faisait pas référence à une instance d'objet réelle).

Pour éviter l'erreur, les objets qui pourraient être nuls doivent être testés pour null avant d'être utilisés.

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
}
Jonathan Wood
la source
96

Sachez que quel que soit le scénario, la cause est toujours la même dans .NET:

Vous essayez d'utiliser une variable de référence dont la valeur est Nothing/ null. Lorsque la valeur est Nothing/ nullpour la variable de référence, cela signifie qu'elle ne contient pas réellement de référence à une instance d'un objet qui existe sur le tas.

Soit vous n'avez jamais affecté quelque chose à la variable, vous n'avez jamais créé d'instance de la valeur affectée à la variable, soit vous définissez la variable égale à Nothing/ nullmanuellement, soit vous avez appelé une fonction qui définit la variable sur Nothing/ nullpour vous.

maître de code
la source
87

Un exemple de cette exception levée est: Lorsque vous essayez de vérifier quelque chose, c'est null.

Par exemple:

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
} 

Le runtime .NET lève une exception NullReferenceException lorsque vous essayez d'effectuer une action sur quelque chose qui n'a pas été instancié, c'est-à-dire le code ci-dessus.

Par rapport à une ArgumentNullException qui est généralement levée comme mesure défensive si une méthode s'attend à ce que ce qui lui est transmis ne soit pas nul.

Plus d'informations sont dans C # NullReferenceException et Null Parameter .

Alex KeySmith
la source
87

Mise à jour C # 8.0, 2019: types de référence nullables

C # 8.0 introduit des types de référence nullables et des types de référence non nullables . Par conséquent, seuls les types de référence nullables doivent être vérifiés pour éviter une exception NullReferenceException .


Si vous n'avez pas initialisé un type de référence et que vous souhaitez définir ou lire l'une de ses propriétés, il lèvera une exception NullReferenceException .

Exemple:

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

Vous pouvez simplement éviter cela en vérifiant si la variable n'est pas nulle:

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

Pour bien comprendre pourquoi une exception NullReferenceException est levée, il est important de connaître la différence entre les types de valeur et les [types de référence] [3].

Donc, si vous avez affaire à des types de valeur , NullReferenceExceptions ne peut pas se produire. Cependant, vous devez rester vigilant lorsque vous traitez avec des types de référence !

Seuls les types de référence, comme son nom l'indique, peuvent contenir des références ou pointer littéralement vers rien (ou «null»). Alors que les types de valeur contiennent toujours une valeur.

Types de référence (ceux-ci doivent être vérifiés):

  • dynamique
  • objet
  • chaîne

Types de valeurs (vous pouvez simplement ignorer ceux-ci):

  • Types numériques
  • Types intégraux
  • Types à virgule flottante
  • décimal
  • bool
  • Structures définies par l'utilisateur
Fabian Bigler
la source
6
-1: puisque la question est "Qu'est-ce qu'une exception NullReferenceException", les types de valeur ne sont pas pertinents.
John Saunders
21
@John Saunders: Je ne suis pas d'accord. En tant que développeur de logiciels, il est vraiment important de pouvoir distinguer les types de valeur et de référence. sinon les gens finiront par vérifier si les entiers sont nuls.
Fabian Bigler
5
C'est vrai, mais pas dans le contexte de cette question.
John Saunders
4
Merci pour l'astuce. Je l'ai un peu amélioré et j'ai ajouté un exemple en haut. Je pense toujours que mentionner les types de référence et de valeur est utile.
Fabian Bigler
5
Je pense que vous n'avez rien ajouté qui ne figurait pas dans les autres réponses, car la question présuppose un type de référence.
John Saunders
78

Un autre cas où NullReferenceExceptionscela peut se produire est l'utilisation (incorrecte) de l' asopérateur :

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

Ici, Booket Carsont des types incompatibles; a Carne peut pas être converti / converti en a Book. Lorsque cette conversion échoue, asrevient null. L'utilisation mybookaprès cela provoque a NullReferenceException.

En général, vous devez utiliser un cast ou as, comme suit:

Si vous vous attendez à ce que la conversion de type réussisse toujours (c'est-à-dire que vous savez ce que l'objet devrait être à l'avance), alors vous devez utiliser un transtypage:

ComicBook cb = (ComicBook)specificBook;

Si vous n'êtes pas sûr du type, mais que vous souhaitez essayer de l'utiliser comme type spécifique, utilisez as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}
Jonathon Reinhart
la source
2
Cela peut se produire souvent lors du déballage d' une variable. Je trouve que cela se produit souvent dans les gestionnaires d'événements après avoir changé le type de l'élément d'interface utilisateur, mais oublie de mettre à jour le code-behind.
Brendan
65

Vous utilisez l'objet qui contient la référence de valeur nulle. Il s'agit donc de donner une exception nulle. Dans l'exemple, la valeur de chaîne est nulle et lors de la vérification de sa longueur, l'exception s'est produite.

Exemple:

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

L'erreur d'exception est:

Exception non-gérée:

System.NullReferenceException: référence d'objet non définie sur une instance d'un objet. à Program.Main ()

user1814380
la source
1
Quelle profondeur! Je n'ai jamais considéré la constante «nulle» comme une valeur de référence. C'est ainsi que C # résume un "NullPointer" hein? B / c si je me souviens en C ++, un NPE peut être provoqué en déréférençant un pointeur non initialisé (c'est-à-dire, ref type en c #) dont la valeur par défaut se trouve être une adresse qui n'est pas allouée à ce processus (dans de nombreux cas, ce serait 0, en particulier dans les versions ultérieures de C ++ qui ont fait l'auto-initialisation, qui appartient au système d'exploitation - f avec lui et mourir de beeotch (ou tout simplement attraper le sigkill avec lequel le système d'exploitation attaque votre processus)).
samis
64

Bien que ce qui provoque une NullReferenceExceptions et des approches pour éviter / corriger une telle exception ait été abordé dans d'autres réponses, ce que de nombreux programmeurs n'ont pas encore appris, c'est comment déboguer indépendamment ces exceptions pendant le développement.

Dans Visual Studio, cela est généralement facile grâce au débogueur Visual Studio .


Tout d'abord, assurez-vous que l'erreur correcte sera détectée - voir Comment puis-je autoriser la rupture sur 'System.NullReferenceException' dans VS2010? Remarque 1

Ensuite, commencez avec le débogage (F5) ou attachez [le débogueur VS] au processus en cours d'exécution . À l'occasion, il peut être utile de l'utiliser Debugger.Break, ce qui vous demandera de lancer le débogueur.

À présent, lorsque l'exception NullReferenceException est levée (ou non gérée), le débogueur s'arrête (rappelez-vous la règle définie ci-dessus?) Sur la ligne sur laquelle l'exception s'est produite. Parfois, l'erreur sera facile à repérer.

Par exemple, dans la ligne suivante, le seul code qui peut provoquer l'exception est if myStrings'évalue à null. Cela peut être vérifié en regardant la fenêtre de surveillance ou en exécutant des expressions dans la fenêtre immédiate .

var x = myString.Trim();

Dans les cas plus avancés, tels que les suivants, vous devrez utiliser l'une des techniques ci-dessus (Windows Watch ou Immédiat) pour inspecter les expressions afin de déterminer si str1était nul ou str2non.

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

Une fois que l'exception a été repérée, il est généralement inutile de raisonner à l'envers pour savoir où la valeur nulle a été [incorrectement] introduite -

Prenez le temps nécessaire pour comprendre la cause de l'exception. Inspectez les expressions nulles. Inspectez les expressions précédentes qui auraient pu entraîner de telles expressions nulles. Ajoutez des points d'arrêt et parcourez le programme selon les besoins. Utilisez le débogueur.


1 Si Break on Throws est trop agressif et que le débogueur s'arrête sur un NPE dans la bibliothèque .NET ou tierce, Break on User-Unhandled peut être utilisé pour limiter les exceptions interceptées. De plus, VS2012 présente Just My Code que je recommande également d'activer.

Si vous déboguez avec Just My Code activé, le comportement est légèrement différent. Lorsque Just My Code est activé, le débogueur ignore les exceptions CLR (Common Language Runtime) de première chance qui sont levées en dehors de My Code et ne passent pas par My Code.

utilisateur2864740
la source
59

Simon Mourier a donné cet exemple :

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

où une conversion unboxing (cast) de object (ou de l'une des classes System.ValueTypeou System.Enum, ou d'un type d'interface) en un type de valeur (autre que Nullable<>) en soi donne le NullReferenceException.

Dans l'autre sens, une boxe conversion d' un Nullable<>qui a HasValueégal false à un type de référence, peut donner une nullréférence qui peut alors conduire plus tard à un NullReferenceException. L'exemple classique est:

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

Parfois, la boxe se déroule d'une autre manière. Par exemple avec cette méthode d'extension non générique:

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

le code suivant sera problématique:

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

Ces cas surviennent en raison des règles spéciales que le runtime utilise lors de la boxe des Nullable<>instances.

Jeppe Stig Nielsen
la source
42

Ajout d'un cas lorsque le nom de classe pour l'entité utilisée dans le cadre d'entité est identique au nom de classe pour un fichier code-behind de formulaire Web.

Supposons que vous ayez un formulaire Web Contact.aspx dont la classe codebehind est Contact et que vous ayez un nom d'entité Contact.

Ensuite, le code suivant lèvera une exception NullReferenceException lorsque vous appelez context.SaveChanges ()

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

Par souci d'exhaustivité, classe DataContext

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

et Classe d'entité de contact. Parfois, les classes d'entités sont des classes partielles afin que vous puissiez également les étendre dans d'autres fichiers.

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

L'erreur se produit lorsque l'entité et la classe codebehind se trouvent dans le même espace de noms. Pour résoudre ce problème, renommez la classe d'entité ou la classe codebehind pour Contact.aspx.

Raison Je ne suis toujours pas sûr de la raison. Mais chaque fois que l'une des classes d'entité étendra System.Web.UI.Page, cette erreur se produit.

Pour la discussion, jetez un œil à NullReferenceException dans DbContext.saveChanges ()

AbhinavRanjan
la source
41

Un autre cas général où l'on pourrait recevoir cette exception concerne les classes de simulation pendant les tests unitaires. Quel que soit le cadre de simulation utilisé, vous devez vous assurer que tous les niveaux appropriés de la hiérarchie des classes sont correctement simulés. En particulier, toutes les propriétés HttpContextréférencées par le code sous test doivent être moquées.

Voir « NullReferenceException levée lors du test de AuthorizationAttribute personnalisé » pour un exemple quelque peu détaillé.

John Saunders
la source
40

J'ai une perspective différente pour répondre à cela. Ce genre de réponses "que puis-je faire d'autre pour l'éviter? "

Lorsque vous travaillez sur différentes couches , par exemple dans une application MVC, un contrôleur a besoin de services pour appeler des opérations commerciales. Dans de tels scénarios, le conteneur d'injection de dépendances peut être utilisé pour initialiser les services afin d'éviter l' exception NullReferenceException . Cela signifie donc que vous n'avez pas à vous soucier de vérifier la valeur null et d'appeler simplement les services à partir du contrôleur comme s'ils seraient toujours disponibles (et initialisés) en tant que singleton ou prototype.

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();
    }
}
Mukus
la source
6
-1: cela ne gère qu'un seul scénario - celui des dépendances non initialisées. Il s'agit d'un scénario minoritaire pour NullReferenceException. La plupart des cas sont de simples malentendus sur le fonctionnement des objets. Viennent ensuite les autres situations où le développeur a supposé que l'objet serait initialisé automatiquement.
John Saunders
L'injection de dépendances n'est généralement pas utilisée afin d'éviter NullReferenceException. Je ne pense pas que vous ayez trouvé un scénario général ici. Dans tous les cas, si vous modifiez votre réponse pour être plus dans le style de stackoverflow.com/a/15232518/76337 , alors je supprimerai le downvote.
John Saunders
38

En ce qui concerne "que dois-je faire" , il peut y avoir de nombreuses réponses.

Une manière plus "formelle" d'empêcher de telles conditions d'erreur lors du développement consiste à appliquer la conception par contrat dans votre code. Cela signifie que vous devez définir des invariants de classe , et / ou même des préconditions et postconditions de fonction / méthode sur votre système, lors du développement.

En bref, les invariants de classe garantissent qu'il y aura des contraintes dans votre classe qui ne seront pas violées en utilisation normale (et par conséquent, la classe ne se mettra pas dans un état incohérent). Les conditions préalables signifient que les données fournies en entrée à une fonction / méthode doivent suivre un ensemble de contraintes et ne jamais les violer, et les post - conditions signifient qu'une sortie de fonction / méthode doit à nouveau suivre les contraintes définies sans jamais les violer. Les conditions du contrat ne doivent jamais être violées lors de l'exécution d'un programme sans bogue, donc la conception par contrat est vérifiée en pratique en mode débogage, tout en étant désactivée dans les versions , pour maximiser les performances du système développé.

De cette façon, vous pouvez éviter les NullReferenceExceptioncas qui résultent d'une violation de l'ensemble de contraintes. Par exemple, si vous utilisez une propriété d'objet Xdans une classe et essayez ultérieurement d'invoquer l'une de ses méthodes et Xque sa valeur est nulle, cela entraînera NullReferenceException:

public X { get; set; }

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

Mais si vous définissez «la propriété X ne doit jamais avoir de valeur nulle» comme condition préalable à la méthode, vous pouvez empêcher le scénario décrit précédemment:

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

Pour cette raison, le projet Code Contracts existe pour les applications .NET.

Alternativement, la conception par contrat peut être appliquée à l'aide d' assertions .

MISE À JOUR: Il convient de mentionner que le terme a été inventé par Bertrand Meyer dans le cadre de sa conception du langage de programmation Eiffel .

Nick L.
la source
2
J'ai pensé ajouter ceci car personne ne l'a mentionné, et dans la mesure où cela existe comme approche, mon intention était d'enrichir le sujet.
Nick Louloudakis
2
Merci d'avoir enrichi le sujet. J'ai donné mon avis sur votre ajout. Maintenant, d'autres peuvent faire de même.
John Saunders
2
J'ai pensé que c'était un ajout intéressant au sujet étant donné qu'il s'agit d'un fil très consulté. J'ai déjà entendu parler de contrats de code et c'était un bon rappel d'envisager de les utiliser.
VoteCoffee
36

Un NullReferenceExceptionest levé lorsque nous essayons d'accéder aux propriétés d'un objet nul ou lorsqu'une valeur de chaîne devient vide et que nous essayons d'accéder aux méthodes de chaîne.

Par exemple:

  1. Lorsqu'une méthode de chaîne d'une chaîne vide accède:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  2. Lorsqu'une propriété d'un objet nul accède:

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 
Hemant Bavle
la source
2
Ceci est une erreur. String.Empty.ToLower()ne lèvera pas d'exception de référence nulle. Il représente une chaîne réelle, bien que vide (c'est-à-dire ""). Puisqu'il a un objet à appeler ToLower(), il ne serait pas logique d'y lancer une exception de référence nulle.
Kjartan du
31

TL; DR: essayez d'utiliser Html.Partialau lieu deRenderpage


J'obtenais Object reference not set to an instance of an objectquand j'ai essayé de rendre une vue dans une vue en lui envoyant un modèle, comme ceci:

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

Le débogage a montré que le modèle était Null dans MyOtherView. Jusqu'à ce que je le change:

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

Et ça a marché.

De plus, la raison pour laquelle je n'avais pas Html.Partialà commencer était parce que Visual Studio lançait parfois des lignes ondulées ressemblant à des erreurs Html.Partials'il se trouvait dans une foreachboucle de construction différente , même si ce n'est pas vraiment une erreur:

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

Mais j'ai pu exécuter l'application sans problème avec cette "erreur". J'ai pu me débarrasser de l'erreur en modifiant la structure de la foreachboucle pour ressembler à ceci:

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

Bien que j'ai le sentiment que c'était parce que Visual Studio lisait mal les esperluettes et les crochets.

Travis Heeter
la source
Vous vouliez Html.Partial, pas@Html.Partial
John Saunders
Veuillez également indiquer quelle ligne a provoqué l'exception et pourquoi.
John Saunders
L'erreur s'est produite dans MyOtherView.cshtml, que je n'ai pas inclus ici, parce que le modèle n'était pas correctement envoyé (c'était le cas Null), donc je savais que l'erreur concernait la façon dont
j'envoyais
22

Que peux-tu y faire?

Il y a beaucoup de bonnes réponses ici expliquant ce qu'est une référence nulle et comment la déboguer. Mais il y a très peu de moyens de prévenir le problème ou du moins de le rendre plus facile à attraper.

Vérifier les arguments

Par exemple, les méthodes peuvent vérifier les différents arguments pour voir s'ils sont nuls et lever un ArgumentNullException, une exception évidemment créée dans ce but précis.

Le constructeur de ArgumentNullExceptioneven prend le nom du paramètre et un message comme arguments afin que vous puissiez dire exactement au développeur quel est le problème.

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

Utiliser des outils

Il existe également plusieurs bibliothèques qui peuvent vous aider. "Resharper" par exemple peut vous fournir des avertissements pendant que vous écrivez du code, surtout si vous utilisez leur attribut: NotNullAttribute

Il y a "Microsoft Code Contracts" où vous utilisez une syntaxe comme celle Contract.Requires(obj != null)qui vous donne l'exécution et la vérification de la compilation: Présentation de Code Contracts .

Il y a aussi "PostSharp" qui vous permettra d'utiliser simplement des attributs comme celui-ci:

public void DoSometing([NotNull] obj)

En faisant cela et en faisant de PostSharp une partie de votre processus de construction, la valeur objnull sera vérifiée lors de l'exécution. Voir: PostSharp null check

Solution de code simple

Ou vous pouvez toujours coder votre propre approche en utilisant de l'ancien code. Par exemple, voici une structure que vous pouvez utiliser pour intercepter des références nulles. Il s'inspire du même concept que 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 };
    }
}

Vous utiliseriez très similaire à la même manière que vous utiliseriez Nullable<T>, sauf dans le but d'accomplir exactement le contraire - de ne pas permettre null. Voici quelques exemples:

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

NotNull<T>est implicitement casté vers et depuis Tafin que vous puissiez l'utiliser presque partout où vous en avez besoin. Par exemple, vous pouvez passer un Personobjet à une méthode qui prend NotNull<Person>:

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

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

Comme vous pouvez le voir ci-dessus, comme avec nullable, vous accéderez à la valeur sous-jacente via la Valuepropriété. Alternativement, vous pouvez utiliser une distribution explicite ou implicite, vous pouvez voir un exemple avec la valeur de retour ci-dessous:

Person person = GetPerson();

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

Ou vous pouvez même l'utiliser lorsque la méthode revient T(dans ce cas Person) en faisant un cast. Par exemple, le code suivant aimerait juste le code ci-dessus:

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

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

Combinez avec l'extension

Combinez NotNull<T>avec une méthode d'extension et vous pouvez couvrir encore plus de situations. Voici un exemple de ce à quoi peut ressembler la méthode d'extension:

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

Et voici un exemple de la façon dont il pourrait être utilisé:

var person = GetPerson().NotNull();

GitHub

Pour votre référence, j'ai rendu le code ci-dessus disponible sur GitHub, vous pouvez le trouver sur:

https://github.com/luisperezphd/NotNull

Fonction de langue associée

C # 6.0 a introduit "l'opérateur null-conditionnel" qui aide un peu à cela. Avec cette fonctionnalité, vous pouvez référencer des objets imbriqués et si l'un d'eux est nulll'expression entière retourne null.

Cela réduit le nombre de contrôles nuls que vous devez effectuer dans certains cas. La syntaxe consiste à mettre un point d'interrogation avant chaque point. Prenons l'exemple du code suivant:

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

Imaginez que countryc'est un objet de type Countryqui a une propriété appelée Stateet ainsi de suite. Si country, State, Countyou Cityest nullalors address will benulle . Therefore you only have to check whetheradresse isnull`.

C'est une excellente fonctionnalité, mais elle vous donne moins d'informations. Cela ne rend pas évident lequel des 4 est nul.

Intégré comme Nullable?

C # a un joli raccourci pour Nullable<T>, vous pouvez rendre quelque chose nullable en mettant un point d'interrogation après le type comme ça int?.

Ce serait bien si C # avait quelque chose comme le NotNull<T>struct ci - dessus et avait un raccourci similaire, peut - être le point d'exclamation pour que vous puissiez écrire quelque chose comme (!): public void WriteName(Person! person).

Luis Perez
la source
2
Ne lancez jamais NullReferenceException
John Saunders
@JohnSaunders oserais-je demander pourquoi? (Mais sérieusement pourquoi?)
Luis Perez
2
NullReferenceException est destiné à être levé par le CLR. Cela signifie qu'une référence à un null s'est produite. Cela ne signifie pas qu'une référence à un null se produirait, sauf que vous avez intelligemment vérifié en premier.
John Saunders
Je vois votre point sur la façon dont cela serait source de confusion. Je l'ai mis à jour vers une exception régulière pour cet exemple et une exception personnalisée dans GitHub.
Luis Perez
Excellente réponse à une question aussi simple. Ce n'est pas si mal quand c'est votre code qui échoue. C'est horrible quand cela vient du plus profond d'une bibliothèque commerciale tierce sur laquelle vous comptez, et le support client insiste pour que ce soit votre code qui cause le problème. Et vous n'êtes pas tout à fait sûr que ce n'est pas le cas et tout le projet doit s'arrêter. Je pense en fait que cela pourrait faire une épitaphe appropriée pour ma pierre tombale: "La référence d'objet n'est pas définie sur une instance d'un objet."
Darrel Lee
10

Fait intéressant, aucune des réponses sur cette page ne mentionne les deux cas de bord, j'espère que personne ne m'en voudra si je les ajoute:

Cas Edge # 1: accès simultané à un dictionnaire

Les dictionnaires génériques dans .NET ne sont pas adaptés aux threads et ils peuvent parfois générer un NullReferenceou même (plus fréquemment) un KeyNotFoundExceptionlorsque vous essayez d'accéder à une clé à partir de deux threads simultanés. L'exception est assez trompeuse dans ce cas.

Cas Edge # 2: code dangereux

Si un NullReferenceExceptionest généré par du unsafecode, vous pouvez regarder vos variables de pointeur et les vérifier IntPtr.Zeroou quelque chose. C'est la même chose ("exception de pointeur nul"), mais dans un code dangereux, les variables sont souvent transtypées en types de valeur / tableaux, etc., et vous vous cognez la tête contre le mur, en vous demandant comment un type de valeur peut jeter ceci exception.

(Une autre raison de ne pas utiliser de code dangereux, sauf si vous en avez besoin, en passant)

jazzcat
la source
5
Votre exemple de dictionnaire n'est pas un cas de bord. Si l'objet n'est pas sûr pour les threads, son utilisation à partir de plusieurs threads produit des résultats aléatoires. Votre exemple de code dangereux diffère de nullquelle manière?
John Saunders
10

Vous pouvez corriger NullReferenceException de manière propre en utilisant des opérateurs Null-conditionnels en c # 6 et écrire moins de code pour gérer les vérifications nulles.

Il est utilisé pour tester null avant d'effectuer une opération d'accès aux membres (?.) Ou d'index (? [).

Exemple

  var name = p?.Spouse?.FirstName;

est équivalent à:

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

Le résultat est que le nom sera nul lorsque p est nul ou lorsque p.Spouse est nul.

Sinon, le nom de la variable se verra attribuer la valeur de p.Spouse.FirstName.

Pour plus de détails: opérateurs null-conditionnels

M.Hassan
la source
9

La ligne d'erreur "Référence d'objet non définie sur une instance d'un objet." Indique que vous n'avez pas affecté d'objet d'instance à une référence d'objet et que vous accédez toujours aux propriétés / méthodes de cet objet.

par exemple: disons que vous avez une classe appelée myClass et qu'elle contient une propriété prop1.

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

Maintenant, vous accédez à ce prop1 dans une autre classe comme ci-dessous:

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

la ligne ci-dessus génère une erreur car la référence de la classe myClass est déclarée mais pas instanciée ou une instance d'objet n'est pas affectée à la référence de cette classe.

Pour résoudre ce problème, vous devez instancier (attribuer un objet à la référence de cette classe).

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;  
     }
}
Jaimin Dave
la source
4

NullReferenceException ou Référence d'objet non définie sur une instance d'un objet se produit lorsqu'un objet de la classe que vous essayez d'utiliser n'est pas instancié. Par exemple:

Supposons que vous ayez une classe nommée Student.

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

Maintenant, considérez une autre classe où vous essayez de récupérer le nom complet de l'élève.

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

Comme vu dans le code ci-dessus, l'instruction Student s - déclare uniquement la variable de type Student, notez que la classe Student n'est pas instanciée à ce stade. Par conséquent, lorsque l'instruction s.GetFullName () est exécutée, elle lèvera l'exception NullReferenceException.

pseudo
la source
3

Eh bien, en termes simples:

Vous essayez d'accéder à un objet qui n'est pas créé ou qui n'est pas actuellement en mémoire.

Alors, comment y faire face:

  1. Déboguez et laissez le débogueur s'arrêter ... Cela vous amènera directement à la variable qui est cassée ... Maintenant, votre tâche est de simplement corriger cela .. En utilisant le nouveau mot-clé à l'endroit approprié.

  2. Si cela est dû à certaines commandes de base de données parce que l'objet n'est pas présent, tout ce que vous avez à faire est de faire une vérification nulle et de le gérer:

    if (i == null) {
        // Handle this
    }
  3. Le plus difficile .. si le GC a déjà collecté l'objet ... Cela se produit généralement si vous essayez de trouver un objet à l'aide de chaînes ... Autrement dit, en le trouvant par le nom de l'objet, il peut arriver que le GC soit déjà nettoyé ... C'est difficile à trouver et deviendra un problème ... Une meilleure façon de résoudre ce problème est de faire des vérifications nulles partout où cela est nécessaire pendant le processus de développement. Cela vous fera gagner beaucoup de temps.

En recherchant par nom, je veux dire qu'un cadre vous permet de FIndObjects en utilisant des chaînes et le code pourrait ressembler à ceci: FindObject ("ObjectName");

Akash Chowdary
la source
3
Si vous avez une référence à un objet, le GC ne le nettoie jamais
John Saunders
2
si vous utilisez des choses comme FindObject ("Nom de l'objet") il n'y a aucun moyen que GC sache à l'avance que vous allez refernece cet objet .. c'est ce qui essayait d'expliquer .. cela se produit au moment de l'exécution
Akash Gutha
2
Il existe des cadres qui fournissent cette fonctionnalité en C # tels que Unity. la question n'a rien à voir avec BCl. Recherchez sur Internet avant de critiquer, il y a une tonne de fonctions comme elles et pour vos informations aimables, je les utilise même quotidiennement. Maintenant, dites-moi comment la réponse n'a aucun sens.
Akash Gutha
2
docs.unity3d.com/ScriptReference/… vérifier le lien et corriger urself mr.expert: p
Akash Gutha
Les exemples que j'ai vus dans votre lien affectent les résultats de GameObject.Find à un champ membre. C'est une référence et le GC ne la collectera pas tant que l'objet contenant n'aura pas été collecté.
John Saunders
1

Littéralement, la façon la plus simple de corriger une NullReferenceExeption a deux façons. Si vous avez un GameObject par exemple avec un script attaché et une variable nommée rb (rigidbody), cette variable commencera nulle lorsque vous démarrez votre jeu.
C'est pourquoi vous obtenez une NullReferenceExeption car l'ordinateur n'a pas de données stockées dans cette variable.

Je vais utiliser une variable RigidBody comme exemple.
Nous pouvons ajouter des données très facilement de plusieurs manières:

  1. Ajoutez un RigidBody à votre objet avec AddComponent> Physique> Rigidbody
    Ensuite, allez dans votre script et tapez rb = GetComponent<Rigidbody>();
    Cette ligne de code fonctionne mieux sous vos fonctions Start()ou Awake().
  2. Vous pouvez ajouter un composant par programme et affecter la variable en même temps avec une ligne de code: rb = AddComponent<RigidBody>();

Notes supplémentaires: Si vous voulez que l'unité ajoute un composant à votre objet et que vous ayez oublié d'en ajouter un, vous pouvez taper [RequireComponent(typeof(RigidBody))]au-dessus de votre déclaration de classe (l'espace en dessous de tous vos usages).
Profitez et amusez-vous à créer des jeux!

CausticLasagne
la source
-1

Si nous considérons des scénarios courants où cette exception peut être levée, l'accès aux propriétés avec un objet en haut.

Ex:

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

ici, si l'adresse est nulle, vous obtiendrez NullReferenceException.

Donc, comme pratique, nous devons toujours utiliser la vérification nulle, avant d'accéder aux propriétés de ces objets (spécialement en générique)

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

Il s'agit essentiellement d'une exception de référence Null . Comme le déclare Microsoft-

Une exception NullReferenceException est levée lorsque vous essayez d'accéder à un membre d'un type dont la valeur est null.

Qu'est-ce que ça veut dire?

Cela signifie que si un membre qui n'a aucune valeur et que nous faisons en sorte que ce membre effectue certaines tâches, le système lancera sans aucun doute un message et dira -

"Hé, attendez, ce membre n'a aucune valeur, il ne peut donc pas exécuter la tâche que vous lui remettez."

L'exception elle-même dit que quelque chose est référé mais dont la valeur n'est pas définie. Cela signifie donc que cela se produit uniquement lors de l'utilisation de types de référence, car les types de valeur ne sont pas nullables.

NullReferenceException ne se produira pas si nous utilisons des membres de type Value.

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

Le code ci-dessus montre une chaîne simple qui est affectée d'un null valeur .

Maintenant, lorsque j'essaie d'imprimer la longueur de la chaîne str , j'obtiens une exception non gérée de type «System.NullReferenceException» , car le membre str pointe vers null et il ne peut pas y avoir de longueur nulle.

' NullReferenceException ' se produit également lorsque nous oublions d'instancier un type de référence.

Supposons que j'ai une classe et une méthode membre. Je n'ai pas instancié ma classe, mais seulement nommé ma classe. Maintenant, si j'essaie d'utiliser la méthode, le compilateur générera une erreur ou émettra un avertissement (selon le compilateur).

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

    }
}

Le compilateur pour le code ci-dessus génère une erreur indiquant que la variable obj n'est pas affectée, ce qui signifie que notre variable a des valeurs nulles ou rien. Le compilateur pour le code ci-dessus génère une erreur indiquant que la variable obj n'est pas affectée, ce qui signifie que notre variable a des valeurs nulles ou rien.

Pourquoi cela se produit-il?

  • NullReferenceException survient en raison de notre faute pour ne pas avoir vérifié la valeur de l'objet. Nous laissons souvent les valeurs des objets non contrôlées dans le développement du code.

  • Elle survient également lorsque nous oublions d'instancier nos objets. L'utilisation de méthodes, de propriétés, de collections, etc. qui peuvent renvoyer ou définir des valeurs nulles peut également être la cause de cette exception.

comment ca peut etre evite?

Il existe différentes façons et méthodes pour éviter cette exception renommée:

  1. Vérification explicite: nous devons respecter la tradition de vérification des objets, propriétés, méthodes, tableaux et collections s'ils sont nuls. Cela peut être simplement implémenté en utilisant des instructions conditionnelles comme if-else if-else etc.

  2. Gestion des exceptions: l'un des moyens importants de gérer cette exception. En utilisant de simples blocs try-catch-finally, nous pouvons contrôler cette exception et également en conserver un journal. Cela peut être très utile lorsque votre application est en phase de production.

  3. Opérateurs nuls: L'opérateur nul coalescent et les opérateurs conditionnels nuls peuvent également être utilisés de manière pratique lors de la définition des valeurs des objets, des variables, des propriétés et des champs.

  4. Débogueur: Pour les développeurs, nous avons la grande arme du débogage avec nous. Si nous rencontrons NullReferenceException pendant la phase de développement, nous pouvons utiliser le débogueur pour accéder à la source de l'exception.

  5. Méthode intégrée: les méthodes système telles que GetValueOrDefault (), IsNullOrWhiteSpace () et IsNullorEmpty () vérifient les valeurs nulles et affectent la valeur par défaut s'il existe une valeur nulle.

Il y a déjà beaucoup de bonnes réponses ici. Vous pouvez également consulter une description plus détaillée avec des exemples sur mon blog .

J'espère que cela aide aussi!

Wasim
la source
Vous avez essentiellement copié la moitié de ce billet de blog et n'ajoutez rien de nouveau que les réponses existantes ne traitent pas.
CodeCaster
@codecaster Est-il dit de copier lorsque vous réécrivez un résumé de votre propre blog. Je sais qu'il n'y a rien de nouveau dans ma réponse et rien de nouveau que les réponses précédentes n'ont pas mais je souhaite apporter une contribution plus sophistiquée et laisser les autres comprendre la façon dont j'ai compris. Sera heureux même si cela aide une seule personne. De bonne foi.
Wasim
-4

Si l'on reçoit ce message lors de l'enregistrement ou de la compilation de la génération, fermez simplement tous les fichiers, puis ouvrez n'importe quel fichier à compiler et à enregistrer.

Pour moi, la raison était que j'avais renommé le fichier et que l'ancien fichier était toujours ouvert.

Harshal Doshi Jain
la source