Answered by:
An unhandled exception of type 'System.NullReferenceException' occurred

Question
-
I'm trying to build a bunch of objects that inherit properties from a base class and set some of the values statically or prebuilt.
Error:
An unhandled exception of type 'System.NullReferenceException' occurred in WBG_Crafting_Core.dll
Additional information: Object reference not set to an instance of an object.I understand what the error is saying but I dont understand why its giving me the error:
Rune recipe:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WBG_Crafting_Core.Base_Items; namespace WBG_Crafting_Core.Recipe { public class Recipe_Rune_a : Base_Recipe { private Ingredients _Ingredients = new Ingredients(); public bool Unlocked { get; set; } public Recipe_Rune_a() { Name = "Recipe Rune a"; Unlocked = false; _Ingredients.Ingredient = "Polished Ancient Stone"; _Ingredients.Amount = 1; Ingredients.Add(_Ingredients); NPC_Sale = 50; NPC_Buy = 3; WBG_Sale = 5; } } }
Base Recipe
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using WBG_Crafting_Core.Base_Items; namespace WBG_Crafting_Core { public class Base_Recipe { /// <summary> /// Recipe Name /// </summary> public string Name { get; set; } /// <summary> /// Ingredient + Amount /// </summary> public List<Ingredients> Ingredients { get; set; } /// <summary> /// NPC Cost Displayed In NPC Shop /// </summary> public long NPC_Sale { get; set; } /// <summary> /// Amount NPC Will give to player for item /// </summary> public long NPC_Buy { get; set; } /// <summary> /// WBG Store Cost /// </summary> public long WBG_Sale { get; set; } } }
Gives an error here:
_Ingredients.Ingredient = "Polished Ancient Stone"; _Ingredients.Amount = 1; Ingredients.Add(_Ingredients);
Its a list of an object and I'm just adding a object to the list. so i'm a little confused.
- Edited by old_School Thursday, March 21, 2019 4:40 PM
Thursday, March 21, 2019 4:39 PM
Answers
-
It's this line right here.
Ingredients.Add(_Ingredients);
Ingredients is null. As DA924x mentions, you really need to step through your code to help better understand what is going wrong. If you get a NRE then stepping through the code will throw you exactly into the line that has the issue. The stack trace associated with the exception (which the debugger shows) contains the line information as well.
But let's pick apart your code. The biggest issue I see with your code is your reuse of the same identifiers over and over again causing more confusion than anything. What is `Ingredients`? In your case it is 2 different identifiers, depending upon context. In one case it is a type you created to, presumably store ingredients. But in the context of Base_Recipe it is also a property. In general this is OK but the confusing part here is that Ingredients is a plural name so a type with a plural name would indicate, to me, it stores a set of values. But it looks like, based upon your usage, it is a single ingredient because Ingredients (the property) is a list of them. In general type names are singular unless they store sets of values. So you might consider renaming your type to singular. Now Base_Recipe would have a List<Ingredient> called Ingredients.
Now let's move on to `Recipe_Rune_a`. It defines a private field called `_ingredients`. So it represents a single ingredient (but it named like multiples). In your constructor for the class you create an instance of the field and then add it to the Ingredients property of the base type. This is where it fails. Ingredients is null. Go back and look at your base class. Nowhere does it ever assign a value to Ingredients so it is always null. Derived types are, at this point, expected to go ahead and initialize Ingredients (from the base class) before they use it.
This isn't really encapsulation though and the base class isn't providing much value. When dealing with sets of items you have to decide who owns the lifetime of that list. Outside of data transfer objects it is rare that you would allow anybody outside a type to alter the list itself (just the contents) and therefore you shouldn't be exposing a setter. The type owning the list should initialize the property and not let it be changed by anybody else.
Here's the quick fix to get around your issue.
public class Base_Recipe { … public List<Ingredients> Ingredients { get { return _ingredients; } } private readonly List<Ingredients> _ingredients = new List<Ingredients>(); }
Base_recipe owns the list and therefore is the only one who creates it. Derived types can simply add to it. You don't need any additional changes in your `Recipe_rune_a` class now. If you're using C# 7+ then you can simplify this code down by using expression bodies but this is optional.
public class Base_Recipe { public List<Ingredients> Ingredients { get; } = new List<Ingredients>(); }
And finally, going back to my original comments about naming and whatnot I'd probably rename `Ingredients` the type to `Ingredient` since it represents a single entity. Then your code reads, to me, easier.
Michael Taylor http://www.michaeltaylorp3.net
- Proposed as answer by Wendy ZangMicrosoft contingent staff Friday, March 22, 2019 6:09 AM
- Marked as answer by Alberto PoblacionMVP Tuesday, April 9, 2019 9:44 PM
Thursday, March 21, 2019 6:14 PM
All replies
-
It means you have to use the Visual Studio debugger, set a breakpoint and start single stepping line by like untill you hit the line that is throwing the exception. The exception means that code is trying to use/efernce an object that is a null value, and the object is not there in memory. You can use Quickwatch to find out what object is a null value at the time of the exception.
Everythin in .NET derives from System.Object the base object for all objects.
Thursday, March 21, 2019 5:06 PM -
It's this line right here.
Ingredients.Add(_Ingredients);
Ingredients is null. As DA924x mentions, you really need to step through your code to help better understand what is going wrong. If you get a NRE then stepping through the code will throw you exactly into the line that has the issue. The stack trace associated with the exception (which the debugger shows) contains the line information as well.
But let's pick apart your code. The biggest issue I see with your code is your reuse of the same identifiers over and over again causing more confusion than anything. What is `Ingredients`? In your case it is 2 different identifiers, depending upon context. In one case it is a type you created to, presumably store ingredients. But in the context of Base_Recipe it is also a property. In general this is OK but the confusing part here is that Ingredients is a plural name so a type with a plural name would indicate, to me, it stores a set of values. But it looks like, based upon your usage, it is a single ingredient because Ingredients (the property) is a list of them. In general type names are singular unless they store sets of values. So you might consider renaming your type to singular. Now Base_Recipe would have a List<Ingredient> called Ingredients.
Now let's move on to `Recipe_Rune_a`. It defines a private field called `_ingredients`. So it represents a single ingredient (but it named like multiples). In your constructor for the class you create an instance of the field and then add it to the Ingredients property of the base type. This is where it fails. Ingredients is null. Go back and look at your base class. Nowhere does it ever assign a value to Ingredients so it is always null. Derived types are, at this point, expected to go ahead and initialize Ingredients (from the base class) before they use it.
This isn't really encapsulation though and the base class isn't providing much value. When dealing with sets of items you have to decide who owns the lifetime of that list. Outside of data transfer objects it is rare that you would allow anybody outside a type to alter the list itself (just the contents) and therefore you shouldn't be exposing a setter. The type owning the list should initialize the property and not let it be changed by anybody else.
Here's the quick fix to get around your issue.
public class Base_Recipe { … public List<Ingredients> Ingredients { get { return _ingredients; } } private readonly List<Ingredients> _ingredients = new List<Ingredients>(); }
Base_recipe owns the list and therefore is the only one who creates it. Derived types can simply add to it. You don't need any additional changes in your `Recipe_rune_a` class now. If you're using C# 7+ then you can simplify this code down by using expression bodies but this is optional.
public class Base_Recipe { public List<Ingredients> Ingredients { get; } = new List<Ingredients>(); }
And finally, going back to my original comments about naming and whatnot I'd probably rename `Ingredients` the type to `Ingredient` since it represents a single entity. Then your code reads, to me, easier.
Michael Taylor http://www.michaeltaylorp3.net
- Proposed as answer by Wendy ZangMicrosoft contingent staff Friday, March 22, 2019 6:09 AM
- Marked as answer by Alberto PoblacionMVP Tuesday, April 9, 2019 9:44 PM
Thursday, March 21, 2019 6:14 PM