NullReferenceException, how do I handle this? - c#

I'm getting this error in C# while trying to build an importer for a tilemap. It's supposed to read files formatted like this;
DatMapName!
6|4
SpritePageID:SpriteID:OffsetX:OffsetY|SpritePageID:SpriteID:OffsetX:OffsetY SpritePageID:SpriteID:OffsetX:OffsetY
SpritePageID:SpriteID:OffsetX:OffsetY|SpritePageID:SpriteID:OffsetX:OffsetY SpritePageID:SpriteID:OffsetX:OffsetY
into a data structure built up like this;
map
MapTile[MapSizeX, MapSizeY]
Tile[Sprites on tile]
- string SpritePageID
- int SpriteID
- Vector2 Offset
Basically it has the map name on the first line, X/Y size on the second, and then it has SpritePageID, spriteID, offsetx, offsetY separated by :, and multiple sprites per tile separated by |. Spaces delimit the different tiles.
However, during processing I get the following error;
Error 1 Building content threw NullReferenceException: Object reference not set to an instance of an object.
at LibTilePipelineExtension.LevelProcessor.Process(String input, ContentProcessorContext context) in C:\Projects\C#\XNA1\TileWorld\LibTilePipelineExtension\LevelProcessor.cs:line 45
at Microsoft.Xna.Framework.Content.Pipeline.ContentProcessor`2.Microsoft.Xna.Framework.Content.Pipeline.IContentProcessor.Process(Object input, ContentProcessorContext context)
at Microsoft.Xna.Framework.Content.Pipeline.BuildCoordinator.BuildAssetWorker(BuildItem item)
at Microsoft.Xna.Framework.Content.Pipeline.BuildCoordinator.BuildAsset(BuildItem item)
at Microsoft.Xna.Framework.Content.Pipeline.BuildCoordinator.RunTheBuild()
at Microsoft.Xna.Framework.Content.Pipeline.Tasks.BuildContent.RemoteProxy.RunTheBuild(BuildCoordinatorSettings settings, TimestampCache timestampCache, ITaskItem[] sourceAssets, String[]& outputContent, String[]& rebuiltContent, String[]& intermediates, Dictionary`2& dependencyTimestamps, KeyValuePair`2[]& warnings)
C:\Projects\C#\XNA1\TileWorld\TileWorld\TileWorldContent\tileworldtest1.tilemap TileWorld
Here's the code that's throwing this error.
string[] lines = input.Split(new char[] { '\n' });
// ------
// Clear out commented lines from the array. Turn it to a list, remove indices, turn back to array.
List<string> derp1 = new List<string>(lines);
for (int derp2 = 0; derp2 < derp1.Count; derp2++) {
if (derp1[derp2][0] == ' ' || derp1[derp2][0] == ';') {
derp1.RemoveAt(derp2);
derp2--;
}
}
lines = derp1.ToArray();
// ------
int rows = Convert.ToInt32(lines[1].Split('|')[0]);
int columns = Convert.ToInt32(lines[1].Split('|')[1]);
MapTile[,] res = new MapTile[rows, columns];
for (int i0 = 2; i0 < (rows + 2); i0++) {
string[] tiles = lines[i0].Split(' ');
for (int i1 = 0; i1 < columns; i1++) {
string[] tileSprites = tiles[i1].Split('|');
--> res[i0 - 2, i1].sprites = new Tile[tileSprites.Length]; <-- Exception
for (int i2 = 0; i2 < tileSprites.Length; i2++) {
string[] spriteData = tileSprites[i2].Split(':');
res[i0 - 2, i1].sprites[i2] = new Tile(spriteData[0], Convert.ToInt32(spriteData[1]), new Vector2(Convert.ToInt32(spriteData[2]), Convert.ToInt32(spriteData[3])));
}
}
}
return new Map(res, lines[0]);
These are the classes I use to make this tree. I removed the using statements to shorten this question.
//Map.cs
namespace LibTile {
public class Map {
public MapTile[,] grid { get; set; }
public string MapName { get; set; }
public Map(MapTile[,] value) {
grid = value;
}
public Map(MapTile[,] value, string name) {
grid = value;
MapName = name;
}
public MapTile GetTile(int row, int col) {
return grid[row, col];
}
public int Rows {
get {
return grid.GetLength(0);
}
}
public int Columns {
get {
return grid.GetLength(1);
}
}
}
}
.
//MapTile.cs
namespace LibTile {
public class MapTile {
public Tile[] sprites { get; set; }
public MapTile(Tile[] value) {
sprites = value;
}
}
}
.
//Tile.cs
namespace LibTile {
public class Tile {
public String SpritePageID;
public int SpriteID;
public Vector2 Offset;
public Tile(String spid, int sid, int ox, int oy) {
SpritePageID = spid;
SpriteID = sid;
Offset = new Vector2(ox,oy);
}
public Tile(String spid, int sid, Vector2 o) {
SpritePageID = spid;
SpriteID = sid;
Offset = o;
}
}
}

You create a new array of MapTile but you never initialize the individual elements. Try this:
res[i0 - 2, i1] = new MapTile(new Tile[tileSprites.Length]);
This will create a new MapTile object in each poition of the array. Not doing this was causing your null reference exception. I would also consider changing the constructor of MapTile to be parameterless and then just set MapTile.sprites as you were doing before.

Related

Why is it possible to set a 2d array even if setter not implemented

Why is it possible to set a 2d array even if the setter not implemented?
Here's my code.
public static void Main()
{
var obj = new TestObj();
obj.qwe[0, 0] = 6;
obj.asd = 7; //error
Console.WriteLine(obj.qwe[0, 0]); //6
}
public class TestObj
{
public int[,] qwe { get; } = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
public int asd { get; } = 4;
}
Why is it possible to set a 2d array even if the setter not implemented?
Because you aren't setting the property, you are updating an item in the array.
For example, this wouldn't compile:
obj.qwe = new int[0, 0];
In your code, you are calling get to obtain a reference to the array, and updating the item at index 0, 0:
obj.qwe[0, 0] = 6;
The property qwe is returning the reference to the array contents. The same applied to 1D or multi-dim arrays. All arrays in C# follow reference semantics
Consider the following
int[,] temp = obj.qwe; // property get
temp[0,0] = 6;
// obj.qwe[0,0] == 6
To prevent this from happening you need to create a wrapper class to the 2D array and implement an indexer.
class Program
{
static void Main(string[] args)
{
IntArray obj = new int[,] { { 1, 2 }, { 3, 4 } };
int x = obj[0, 0]; // ok, getter defined
obj[0, 0] = 10; // error, no setter defined
int[,] objCopy = obj; // assignment to int[,] makes a copy
objCopy[0, 0] = 10; // ok to modify copy
}
}
Use the integer array wrapper class below:
public class IntArray
{
readonly int[,] data;
public IntArray(int rows, int columns)
{
this.data = new int[rows, columns];
this.Rows = rows;
this.Columns = columns;
}
public IntArray(int[,] data)
{
this.data = data;
this.Rows = data.GetLength(0);
this.Columns = data.GetLength(1);
}
public int this[int row, int column]
{
// indexer getter
get => data[row, column];
}
public int Rows { get; }
public int Columns { get; }
public int[,] ToArray()
{
int[,] copy = new int[Rows, Columns];
Array.Copy(data, copy, data.Length);
return copy;
}
public static implicit operator IntArray(int[,] array)
=> new IntArray(array);
public static implicit operator int[,](IntArray obj)
=> obj.ToArray();
}
The reason why this is the case has already been answered by others.
To make the property read only I would suggest creating your own class for 2D arrays. This allow you to implement a read only interface, and it is fairly easy to do:
public interface IReadOnlyArray2D<out T>
{
T this[int x, int y] { get; }
}
public class MyArray2D<T> : IReadOnlyArray2D<T>
{
public int Width { get; }
public int Height { get; }
public T[] Data { get; }
public MyArray2D(int width, int height )
{
this.Width = width;
this.Height = height;
Data = new T[width * height];
}
public T this[int x, int y]
{
get => Data[y * Width + x];
set => Data[y * Width + x] = value;
}
}
There are other advantages of using a regular 1D array as the backing storage:
Sometimes you only want to process all items, and do not care about the x/y coordinates.
Multidimensional arrays can sometimes be slow if it is used incorrectly.
Interoperability is often easier since more systems use 1D arrays or pointers when exchanging data.
creating a multidimensional array like new T[width, height] will store it in column-major order, while most image-like data is usually stored row-major order.

Having trouble creating random values

So I created Pairs class that contains int and double and I want to create an array of them with my
array class by creating random values, but I'm getting System.NullReferenceException at Line 19 of
my array class.
Here's my pair class
class Pair
{
public int integer = 0;
public double doubl = 0.0;
public Pair(int integer, double doubl)
{
this.integer = integer;
this.doubl = doubl;
}
public Pair()
{
}
public int Integer() { return integer; }
public double Doubl() { return doubl; }
}
And this is my array class and the abstract class
class MyDataArray : DataArray
{
Pair[] data;
int operations = 0;
public MyDataArray(int n, int seed)
{
data = new Pair[n];
Random rand = new Random(seed);
for (int i = 0; i < n; i++)
{
data[i].integer = rand.Next(); //I get error here
data[i].doubl = rand.NextDouble();
}
}
public int integer(int index)
{
return data[index].integer;
}
public double doubl(int index)
{
return data[index].doubl;
}
}
abstract class DataArray
{
public int operations { get; set; }
public abstract void Swap(int i, int j);
public abstract void Swap2(int i, int high);
}
Also is it even worth it using this abstract class I used this from a reference that my university
provided. I have to create an quicksort algorithm that sorts pairs in arrays and linked lists and analyze it.
data = new Pair[n];
This creates a new array of null references.
The loop should be
for (int i = 0; i < n; i++)
{
data[i] = new Pair(rand.Next(), rand.NextDouble())
}
While we are looking at your code: you are making a good attempt here to make an immutable pair, but it could be better. What you want is:
class Pair
{
public Pair(int integer, double doubl)
{
this.Integer = integer;
this.Double = doubl;
}
public int Integer { get; private set; }
public double Double { get; private set; }
}
Shorter, safer, clearer.
The issue with your code is that you are only initializing the array of data in MyDataArray. When creating an array of instances, it only initializes references for the array, not the actual instances to be in the array. Those references all point to null. So when you do try to set the integer member of the i'th Pair instance in the data array:
...
data[i].integer = rand.Next();
...
You are actually trying to set the integer member of null, which does not exist.
...
null.integer = rand.Next();
...
To fix this, simply create a new instance of Pair for each index of data in your loop.
...
for (int i = 0; i < n; i++)
{
data[i] = new Pair();
data[i].integer = rand.Next();
data[i].doubl = rand.NextDouble();
}
...
Even better, you can use the constructor you've made where it takes parameters to set integer and doubl upon construction to simplify the code in your loop.
...
for (int i = 0; i < n; i++)
{
data[i] = new Pair(rand.Next(), rand.NextDouble());
}
...

Access updated array from constructor

I have a parameterized constructor and a default constructor. They both create a new object array with x length, however, when I try to access the array in the Add method, it returns the value "null". I can't initialize the array in the fields because I don't know what size the user wants it to be, but I don't know how to access the 'updated' array later in the code. I get a NullReferenceException() on the line of code: if (count > data.Length) because data has the value null.
class CustomList
{
private int count;
private String[] data;
public int Count
{
get { return count; }
}
public CustomList(int arrayNum)
{
String[] data = new String[arrayNum];
}
public CustomList(): this(4)
{
}
public void Add (String item)
{
if (count > data.Length)
{
String[] temp = new String[count * 2];
for (int i = 0; i < data.Length; i++)
{
temp[i] = data[i];
}
data = temp;
}
data[count] = item;
count++;
}
Change this:
public CustomList(int arrayNum)
{
String[] data = new String[arrayNum];
}
To this:
public CustomList(int arrayNum)
{
data = new String[arrayNum];
}
You have accidentally created a local variable in the constructor which is being assigned to instead of the field that you wanted to assign to.
Change your code.
Your data object in constructor is local variable.
And you are not initializing your instance data object.
class CustomList
{
private int count;
private String[] data;
public int Count
{
get { return count; }
}
public CustomList(int arrayNum)
{
data = new String[arrayNum];
}
public CustomList(): this(4)
{
}
public void Add (String item)
{
if (count > data.Length)
{
String[] temp = new String[count * 2];
for (int i = 0; i < data.Length; i++)
{
temp[i] = data[i];
}
data = temp;
}
data[count] = item;
count++;
}

encapsulation of an array of objects c#

I would like to create an array of objects. Each object has it's own int array.
For each object I assign values to it's array ONLY with keys given by myself (example: li[i].V[10] = 1; li[i].V[50] = 10; )
Can someone tell me how to do that? Can I do that without using Lists?
The second case is analogical to first. I would like to know how to assign values of object's List
using setter.
I tried to do that by myself. Unfortunately My code crashed cuz I don't know how to set the dimension of V and Word:
class CFiles
{
//private int[] v=new int[5];//dont want to specify the dimention of array here
private int[] v;//vector of file
private List<string> words;
public CFiles()
{
words = Words;
v = new int[50];
v = V;
}
public int[] V { get; set; }
public List<string> Words { get; set; }
}
class Program
{
static void Main(string[] args)
{
CFiles[] li = new CFiles[2];
for(int i=0;i<li.Length;i++)
{
li[i]=new CFiles();
li[i].V[10] = 1;
li[i].V[50] = 10;
li[i].V[50] = 15;
li[i].Words.Add("a");
li[i].Words.Add("ab");
li[i].Words.Add("abc");
}
for (int i = 0; i < li.Length; i++)
{
for(int j=0;j<li[i].V.Length;j++)
{
Console.WriteLine(li[i].V[j]);
}
}
Console.WriteLine();
}
}
Your constructor isn't right and your properties aren't quite right. You might want something more like this:
class CFiles
{
//private int[] v=new int[5];//dont want to specify the dimention of array here
private int[] v;
public int[] V { get { return v; } set { v = value; } }
private List<string> words;
public List<string> Words { get { return words; } set { words = value; } }
public CFiles()
{
words = new List<string>();
v = new int[51]; //needs to be 51 if you are going to assign to index 50 below
}
}
Other than those issues, your code seems to do what you want. You have an array of objects where each object has its own int array (in addition to a string of strings).
Is that not what you want?

How to set a list within an array

I initiated a multidimensional array:
private byte[,] grid = new byte[9, 9];
I want to hold a byte List with some values, for each cell withing the existing grid.
I know a 3 dimensional array would be handy here. But since the list with invalid values is dynamical, I would like to use a List instead of an array.
Edit: To give some context here. I'm making a sudoku, which is represented by a multidimensional array. Since I would like to solve the sudoku programmatically and I'm using the backtrack algorithm, I need to remember the digits that where invalid for each cell.
So each cell should consist of a value and a List. I could probably just put the actual field as first value in the list. But never know if there is an actual clean solution :)
var grid = new List<byte>[9,9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
grid[i, j] = new List<byte>();
After that every item of 9x9 array contains an empty List<byte>.
However, I would suggest creating a class like Field
public class Field
{
public byte Value { get; set; }
public List<byte> List { get; set; }
public Field()
{
List = new List<byte>();
}
}
And use it instead of just List<byte>:
var grid = new Field[9, 9];
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
grid[i, j] = new Field();
If I understand you correctly you need a list of bytes in a list?
Try this:
List<List<byte>> listOfBytesInList = new List<List<byte>>();
Update
You can implement a own class that uses internal a list of bytes.
I'm too late, but regardless I post my solution anyway.
public class ByteFieldList
{
private readonly List<byte> _interalBytes;
public ByteFieldList()
: this(4, 0)
{
}
public ByteFieldList(byte fieldValue)
: this(4, fieldValue)
{
}
public ByteFieldList(int capacity, byte fieldValue)
{
FieldValue = fieldValue;
_interalBytes = new List<byte>(capacity);
}
public byte FieldValue { get; set; }
public ByteFieldList Add(byte b)
{
_interalBytes.Add(b);
return this;
}
public void AddRange(byte[] bytes)
{
foreach (var b in bytes)
Add(b);
}
public ByteFieldList Remove(byte b)
{
_interalBytes.Remove(b);
return this;
}
public byte this[int index]
{
get { return _interalBytes[index]; }
}
public byte[] GetAllInvalid()
{
return _interalBytes.ToArray();
}
}
public void Usage()
{
ByteFieldList byteFieldList = new ByteFieldList(5);
byteFieldList.Add(5).Add(4).Add(8);
byteFieldList.AddRange(new byte[] { 7, 89, 4, 32, 1 });
var invalids = byteFieldList.GetAllInvalid(); // get 5, 4, 8, 7, 89, 4, 32, 1
byteFieldList.FieldValue = 4;
}

Categories

Resources