Here's a piece of code coming from a C# project using a 2d array. For a reason I don't understand my program compiles perfectly but during run-time it crashes.
public class Tile_Info
{
public int id;
public Tile_Info(int _id)
{
id = _id;
}
}
class Program
{
public static void Main(string[] args)
{
int width = 20;
int height = 30;
Tile_Info[,] my_tile;
my_tile = new Tile_Info[width, height];
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++)
{
my_tile[x, y].id = 0;
}
}
}
}
According to the debugger it's because "Object reference not set to an instance of an object", but I'm pretty sure it's what I'm doing here: my_tile = new Tile_Info[width, height];.
Anyone can tell what's wrong? Thank you for your support!
The creation of the array does not create the objects themselves, just like a creation of a parking lot does not create the cars that park there.
You still need to create the objects yourself. Change
my_tile[x, y].id = 0;
to
my_tile[x, y] = new Tile_Info(0);
This only happens when reference types (class) are used because the thing that is stored in the array is a reference to an instance, instead of the instance itself. On a lower level this (more or less) means that the memory for the instance is not yet allocated, just the memory for its reference, so you must new up an instance to initialize it. On the other hand if Tile_Info is a value type (struct) then the array will contain the actual instance, and the new Tile_Info[width, height] would have initialized the allocated memory to valid start state (all zeroes), which is exactly what the default parameterless constructor of a value type does.
So, if you had defined Tile_Info like this:
public struct Tile_Info
{
public int id; // this should be a property, public fields are baaad
public Tile_Info(int _id){ id = _id;}
}
both my_tile[x, y].id = 0 and my_tile[x, y] = new Tile_Info(0) would have been legal.
You have created a new 2D Tile_Info array by the first new keyword.
You have only created an empty array that can hold items of type Tile_Info. Its your job to create individual items and put them in the array.
Related
I am trying to add a few different members to a list, but when the list is added to it contains copies of only the last member added:
private PotentialSolution tryFirstTrack(PotentialSolution ps, List<PotentialSolution> possibleTracks)
{
for (Track trytrack = Track.Empty + 1; trytrack < Track.MaxVal; trytrack++)
{
if (validMove(ps.nextSide, trytrack))
{
ps.SetCell(trytrack);
possibleTracks.Add(ps);
}
}
return tryNextTrack(ps, possibleTracks);
}
The PotentialSolution class looks like this:
public class PotentialSolution
{
public Track[,] board;
public Side nextSide;
public int h;
public int w;
static int cellsPerSide;
static bool testing;
static int minTracks;
.....
public void SetCell(Track t)
{
board[h, w] = t;
}
}
So we are trying to make several copies of the board which only differ by which 'track' is placed in the current cell.
If I have a breakpoint at possibleTracks.Add(ps) then I can see by inspecting ps that the required cell contents is changing each time, as required.
But when the code reaches the next line (or the return statement), the cell content is the same in each member of the list (it's the last one that was added).
What I am doing wrong here? I have tried using an ArrayList and also a basic array instead, but get the same result. It's acting as though the board member is decared as static, but it's not.
[edit]
In response to those who suggested making copies of ps, you are correct and I had tried this before - but only tried single-stepping after the change and didn't run the full program (this method is used hundreds of times). When running the full program, making copies of ps certainly makes a difference to the result (although it's still not correct). The problem now, and why I didn't stick with using the copies, is that an added test still shows the list to contain the same versions of ps, even though the debugger has shown 2 or 3 different tracks being deployed:
private PotentialSolution tryFirstTrack(PotentialSolution ps, List<PotentialSolution> possibleTracks)
{
for (Track trytrack = Track.Empty + 1; trytrack < Track.MaxVal; trytrack++)
{
if (validMove(ps.nextSide, trytrack))
{
PotentialSolution newps = new PotentialSolution(ps);
newps.SetCell(trytrack);
possibleTracks.Add(newps);
}
}
// temporary test, can be removed
if (possibleTracks.Count >= 2)
{
PotentialSolution ps1 = new PotentialSolution(possibleTracks.First());
PotentialSolution ps2 = new PotentialSolution(possibleTracks.Last());
if (ps1.GetCell() != ps2.GetCell())
{
// should always get here but never does
int foo = 1;
}
}
return tryNextTrack(ps, possibleTracks);
}
By the way, Track and nextSide are just enum integers, they will be 0-6, and the list will contain 0,1,2,or 3 members, never more.
You are adding references to the same object: ps in possibleTracks.Add(ps)
You could add a constructor to PotentialSolution duplicating the class:
public class PotentialSolution
{
public Track[,] board;
public Side nextSide;
public int h;
public int w;
static int cellsPerSide;
static bool testing;
static int minTracks;
//.....
public PotentialSolution()
{
}
public PotentialSolution(PotentialSolution ps)
{
board = ps.board;
nextSide = ps.nextSide;
h = ps.h;
w = ps.w;
}
//.....
Then use:
private PotentialSolution tryFirstTrack(PotentialSolution ps, List<PotentialSolution> possibleTracks)
{
for (Track trytrack = Track.Empty + 1; trytrack < Track.MaxVal; trytrack++)
{
if (validMove(ps.nextSide, trytrack))
{
ps.SetCell(trytrack);
possibleTracks.Add(new PotentialSolution(ps)); // duplicate object
}
}
return tryNextTrack(ps, possibleTracks);
}
This creates a new instance of the class each time it is added to the list.
Consider giving the PotentialSolution type value semantics by making it a struct and implementing a Clone method, or a constructor that takes another PotentialSolution as an argument. Also, to clone a 2D array of value types, call Object.Clone() and cast the result to T[,].
When making a copy of your PotentialSolution, you'll need to make sure your clone your board array, because, in your case, each PotentialSolution keeps its own representation of the state of the board.
I feel like the critical part you're missing is how to shallow clone a 2D array, which in general, is:
T[,] copy = (T[,])original.Clone();
WARNING: Clone creates a shallow copy of the array. For value-types this copies the values of each element, so for your int-like "Track" type it does what you want, but for other readers who may be using reference-types (like classes) it does not clone each object referred to by each element of the array. The elements of the new array are just object references, and will still refer to the same objects referred to by the elements of the original array. See the documentation.
Full example below that changes the middle cell of a 3x3 board from A to B.
using System;
using System.Linq;
public enum Track { A, B, C }
public enum Side { X, Y, Z }
public struct PotentialSolution
{
public Track[,] board;
public Side nextSide;
public int h;
public int w;
public void SetCell(Track t)
{
board[h, w] = t;
}
public PotentialSolution(Track[,] board, Side nextSide, int h, int w)
{
this.board = (Track[,])board.Clone();
this.nextSide = nextSide;
this.h = h;
this.w = w;
}
public PotentialSolution Clone()
{
return new PotentialSolution(board, nextSide, h, w);
}
// This `ToString` is provided for illustration only
public override string ToString()
{
var range0 = board.GetLength(0);
var range1 = board.GetLength(1);
var b = board;
return string.Join(",",
Enumerable.Range(0, range0)
.Select(x => Enumerable.Range(0, range1)
.Select(y => b[x, y]))
.Select(z => "[" + string.Join(",", z) + "]"));
}
}
class Program
{
static void Main(string[] args)
{
Track[,] someBoard = new Track[3, 3];
PotentialSolution ps1 = new PotentialSolution(someBoard, Side.X, 1, 1);
ps1.SetCell(Track.A);
PotentialSolution ps2 = ps1.Clone();
ps2.SetCell(Track.B);
Console.WriteLine(ps1);
Console.WriteLine(ps2);
}
}
I'm filling in the blanks liberally, so please excuse any assumptions I have made that differ from your actual situation, because I have done so only to make this example self-contained. My ToString implementation and its usage of System.Linq is not necessary; it's purely for the purposes of displaying the 2D array in my example.
You always call SetCell on the same ps object you received as a parameter then add the same instance to the possibleTracks list. The result is: possibleTrack contains ps n times and because it is the same instance you used in each cycle it will have the last change you applied via SetCell call.
Not sure what you wanted to achieve but it looks you need a modified copy of ps in each cycle for adding to possibleTrack list. Making PotentialSolution a struct instead of class could be enough? Structs are copied in such a way but may hit your performance if PotentialSolution is big.
The board member will still generate the same problem, because despite ps will be copied but the board inside it will contain same Track references. The trick can be applied to Track too, but the performance issues may raise more.
Just implement a Clone on PotentialSolution to have fully detached instances of it, then call ````SetCell``` on cloned instance and add that instance to the list.
I have a 'Movie' class in my C# code that has an int[] ratings = new int[10]; as a field. I would like to place numbers in this empty array from my main program.
For that, I would need a method, that could point to the actual free index of the array to put the integer there, but the other integer that would point to the free index would be reset to 0 everytime the method is called. Thus, my question is, that how can I place an integer in my method that is increased everytime the method was called.
This is the method in the class:
public void Rate(int rating)
{
int x = 0;
ratings[x] = rating;
}
This is how I call it in the main program
Movie asd = new Movie(blabla...);
Rate.asd(1);
Rate.asd(1);
Rate.asd(1);
So I called it 3 times, and I would want the 'x' integer in the class's method to increase.
Thanks in advance.
First of all, you have an error in the code you have posted.
As I suppose rather than:
Movie asd = new Movie(blabla...);
Rate.asd(1);
Rate.asd(1);
Rate.asd(1);
you want to paste here:
Movie asd = new Movie(blabla...);
asd.Rate(1);
asd.Rate(1);
asd.Rate(1);
As C# does not allow to use static method variables (like i.e. C++ does) you have two options:
first, make x value (from Rate method) a Movie's class variable, so Rate method will "remember" the next index,
second (and better) rather than intiger array - if possible use any kind of list or queue (which can manage indexing for you).
The problem is that local variables are discarded when exiting a method.
class SomeClass
{
private int x = 42;
public void DoSometing(int y)
{
int a = y + 5;
x += a * a;
// a stops to exist here
}
}
Solution is to store the variable in the containing class as well
class SomeOtherClass
{
private int x = 42;
private int a = 0;
public void DoSomething(int y)
{
a = y + 5;
x += a * a;
}
}
Now SomeOtherClass remembers the value of a. That's basically the point of member variables a.k.a. fields - to store the state of the object.
More appropriate for your problem:
class ClassWithAnArrayAndCount
{
private int[] values = new int[10];
private int taken = 0;
public void Add(int value)
{
if (taken == 10)
throw new ArgumentOutOfRangeException(); // sorry, no more space
values[taken++] = value;
}
public int Taken { get { return taken; } }
}
I have a class which should be immutable in this class i have only get indexer a private set property so why this is not immutable and i can set some field in array as you could see in main class...
class ImmutableMatice
{
public decimal[,] Array { get; private set; } // immutable Property
public ImmutableMatice(decimal[,] array)
{
Array = array;
}
public decimal this[int index1, int index2]
{
get { return Array[index1, index2]; }
}
........
and in main method if i fill this class with data and change the data
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999;
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
}
Is there any way how to make this class immutable?
Ah yes the solution was copy array to new array in constructor like this:
public ImmutableMatice(decimal[,] array)
{
decimal[,] _array = new decimal[array.GetLength(0),array.GetLength(1)];
//var _array = new decimal[,] { };
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
_array[i, j] = array[i, j];
}
}
Array = _array;
}
That is because you are actually changing the data in the ARRAY, rather than the indexer.
static void Main(string[] args)
{
decimal[,] testData = new[,] {{1m, 2m}, {3m, 4m}};
ImmutableMatice matrix = new ImmutableMatice(testData);
Console.WriteLine(matrix[0,0]); // writes 1
testData[0, 0] = 999; // <--- THATS YOUR PROBLEM
Console.WriteLine(matrix[0,0]); // writes 999 but i thought it should
// write 1 because class should be immutable?
}
You can copy the array into your private property in the constructor to avoid this situation.
Note that you indeed cannot write matrix[0,0] = 999; because the indexer has no setter.
Edit
As Chris pointed out (how could I have missed it myself?) - you shouldn't expose the array as a property at all (which means in most cases it doesn't even have to be a property).
Consider the following code instead:
private decimal[,] _myArray; // That's private stuff - can't go wrong there.
public decimal this[int index1, int index2]
{
// If you only want to allow get data from the array, thats all you ever need
get { return Array[index1, index2]; }
}
Your class is immutable, but the objects inside it aren't.
Having public decimal[,] Array { get; private set; } will only guarantee that you cannot set the property Array to a new instance of Array, but it does not prevent you from accessing the existing object and changing its values (which aren't immutable).
You might want to look into the appropriately named ReadOnlyCollection<T> class.
As #Mike pointed out and I looked past the first time: there's a twist to this because you are accessing the value through the testData object and not through matrix. While the original point still stands, it is more exact to say that the problem you have is that you are changing values in the underlying object which has its reference passed around. You're bypassing the ImmutableMatice object alltogether.
The beforementioned solution of using a ReadOnlyCollection<T> still stands: by creating this read-only wrapper around it, you won't be able to change it anymore afterwards. Howver this is only the case when you actually use it the way its intended: through ImmutableMatice and not through the underlying collection which you still have a reference to.
Another solution that solves this problem is to copy the contents of the original array to another one to "disconnect" it from the array your still have a reference to.
In order to illustrate this, consider the following samples. The first one demonstrates how the underlying reference can still be influenced while the second one shows how it can be solved by copying your values to a new array.
void Main()
{
var arr = new[] { 5 };
var coll = new ReadOnlyCollection<int>(arr);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 1
}
void Main()
{
var arr = new[] { 5 };
var arr2 = new int[] { 0 };
Array.Copy(arr, arr2, arr.Length);
var coll = new ReadOnlyCollection<int>(arr2);
Console.WriteLine (coll[0]); // 5
arr[0] = 1;
Console.WriteLine (coll[0]); // 5
}
I am trying to create a Sudoku in WinForms with C# as a school assignment. Everything in the sudoku MUST be object oriented so I have not chosen to structure the code like this, the teacher did.
When I put a number(int) in a Textbox in the SudokuGUI, it tries to put the number in the arrays but fails and give me the error well known:
An unhandled exception of type 'System.NullReferenceException'
occurred in WindowsFormsApplication5.exe Additional information:
Object reference not set to an instance of an object.
This is how the code look like:
First we send the Number when keyreleased from the TextBox to the method that will put the number in to the array
private void Valuechange_KeyUp(object sender, KeyEventArgs e)
{
TextBox text_box = sender as TextBox;
var position = tableLayoutPanel1.GetPositionFromControl(text_box);
int x = position.Row;
int y = position.Column;
if (int.TryParse(text_box.Text, out value) && int.Parse(text_box.Text) < 10 && int.Parse(text_box.Text) > 0 || value == 0)
{
add_value.Array_AddNumber(x, y, value);
}
else
{
MessageBox.Show("Skriv in en siffra mellan 1-9");
text_box.Clear();
}
}
Here is the method that will add the number from Textbox to the Array that will hold the numbers
class Ruta
{
Siffra number = new Siffra();
public Siffra[,] SudokuArray = new Siffra[9, 9];
public void Array_AddNumber(int x, int y, int value)
{
SudokuArray[x, y].nummer = value;
}
}
And here is the "Siffra" which means Number in Swedish, that is the the type of the Array
class Siffra
{
private int _nummer;
public int nummer
{
get { return _nummer; }
set { _nummer = value; }
}
}
What have I done wrong, I really don't understand, my teacher couldn't even help me :/
Here is the whole solution:
https://dl.dropboxusercontent.com/u/13409794/WindowsFormsApplication5.zip
The problem is a misunderstanding of this line:
public Siffra[,] SudokuArray = new Siffra[9,9];
That line creates a new 2-dimensional array object in memory, with space for 9 items x 9 items (81 in total). The misunderstanding is that the contents of each spot in the array is still null. Therefore, later on in your code, when you do this:
SudokuArray[x,y].nummer = value;
The code first looks up the array reference and uses that to find the element at position (x,y). That value is still null. The code then ties to use the nummer property of a null reference. Oops. You can't do that.
To fix it, you need to add this code to the constructor for your Ruta class:
for (int x = 0; x < 9; x++)
for (int y = 0; y < 9; y++)
SudokuArray[x,y] = new Siffra();
Since SudukuArray isn't null, the problem (the null value) must be the thing in it.
Siffra is a class - a reference type. That means instances of it are null by default (unlike structs, or value types).
So when you create a 9x9 array of them, you are creating a 9x9 array of nulls.
The rest is homework.
You are initializing the array:
public Siffra[,] array = new Siffra[9,9];
But never creating individual Siffra instances. Therefore, when you attempt to access one, you are actually getting a null. You then attemp to get a nummer from the null instance... which leads to the exception.
Solution
Initialize each instance in the array before you use it:
for(int i=0; i<9; i++)
for(int j=0; j<9; j++)
array[i,j] = new Siffra();
You have allocated the array to have a size that can hosts 9x9 Siffra, and that is right, but the 81 slots present in the array are all NULL.
None contains a Siffra so, when your code executes
SudokyArray[x,y].nummer = value;
it is like you are writing
null.nummer = value;
of course this is a NullReferenceException
Somewhere, possibly in the constructor of your class Ruta you need to fill the array with 81 instances of the class Siffra
class Ruta
{
public Siffra[,] SudokyArray;
public Ruta()
{
SudokyArray = new Sufra[9,9]
for(int i = 0; i < 9; i++)
{
for(int y = 0; y < 9; y++)
SudokuArray[i, y] = new Suffra();
}
}
}
What I'm trying to do is define my own type that contains 2 ints to use in a 2-dimensional array. The application for this is using the array indexes as x,y coordinates for objects in 2-d space to be displayed. So object with data stored at array[13,5] would be displayed at x=13,y=5, and the properties of that object could be retrieved with array[13,5].property1, for example. The type I've defined is extremely simple:
chunkBlocks.cs:
public class chunkBlocks {
public int blockType;
public int isLoaded;
}
then, I initialize the array:
chunkBlocks[,] _bData = new chunkBlocks[17, 17];
This all compiles/runs without error. The NRE is thrown when I try to assign a value to one of the properties of the type. For debugging, I have the code written as:
_bData[i, n].blockType = 5;
and the NRE is thrown specifically on the .blockType portion. I've tried changing the type to initialize with 0 values for both ints to no avail:
public class chunkBlocks {
public int blockType = 0;
public int isLoaded = 0;
}
I've Googled around and searched SO, and I've not been able to turn up anything. I'm sure it's a relatively simple matter, but I'm not experienced enough to be able to pinpoint it.
Thanks!
You need to initialize every instance of the array:
_bData[i, n] = new chunkBlocks();
Now assign the value to it:
_bData[i, n].blockType = 5;
You will have to initialize every instance, you just have declared them in the array.
I think you should do this:
for(int i = 0;i<17;i++)
{
for (int j = 0; j < 17; j++)
{
_bData[i, j] = new chunkBlocks ();
}
}