I'm looking for a way to replace some of my List objects with arrays in my project to boost performance. The reasoning is that the List I would replace do not change size often (sometimes only once), so it would make sense to swap them out with arrays. Also, I would like to prevent allocations on the heap as much as possible.
So my idea is to create a struct (ArrayStruct) with two members "Count" and "Array". The struct would have some functions to add/remove/get/set/ect... elements in the array. The count would keep count of the elements that are usable in the array, and the array would only increase in size, never decrease.
Note that this is for a very particular case because I know the array size won't be very big.
The main problem I have is when I pass the reference to an already existing ArrayStruct object (named a in the example) to another (named b in the example) - I can edit the array in it, but once I resize it in a, I get an out of range exception in b. Is this because of the way I'm updating the reference via System.Array.Resize, so that the object b somehow does not attribute this change?
The only thing I could come up with as an idea was trying to override the assignment operator so that it would create a new array on assignment... but obviously that does not work.
My ArrayStruct
public struct ArrayStruct<T>
{
[SerializeField] private int m_Count;
[SerializeField] private T[] m_Array;
public int Count => m_Count;
public void Initialize(int startSize)
{
m_Count = 0;
m_Array = new T[startSize];
}
public T GetAt(int index)
{
return m_Array[index];
}
public void SetAt(int index, T newValue)
{
m_Array[index] = newValue;
}
public void Add(T newElement)
{
if (m_Array == null) {
m_Array = new T[1];
} else if (m_Array.Length == m_Count) {
System.Array.Resize(ref m_Array, m_Count + 1);
}
m_Array[m_Count] = newElement;
m_Count++;
}
public void Remove(T element)
{
for (int i = 0; i < m_Count; ++i) {
if (!element.Equals(m_Array[i])) {
continue;
}
for (int j = index; j < m_Count - 1; ++j) {
m_Array[j] = m_Array[j + 1];
}
m_Count--;
m_Array[m_Count] = default(T);
break;
}
}
//Trying to overload the = operating by creating a auto cast, this gives a compile error
/*public static implicit operator ArrayStruct<T>(ArrayStruct<T> otherArray)
{
var newArray = new ArrayStruct<T>();
newArray.m_Count = otherArray.Count;
newArray.m_Array = new T[otherArray.Length];
otherArray.m_Array.CopyTo(newArray.m_Array);
return newArray;
}*/
An example showcasing the problem
var a = new ArrayStruct<string>()
a.Add("hello");
var b = a;
print (b.GetAt(0)); //"hello"
a.SetAt(0, "new value for a");
print(b.GetAt(0));//"new value for a" , changing a changed b because the array is the same in both, this is bad
a.Add("resizing array");
print (b.GetAt(1)); //"Out of range exception" , because the array was resized for a but not for b therefore the connection broke
So do any of you have an idea of how I could make a new copy the array when I assign the struct to another variable?
Of course, I know I could use a function to do something like so
b = new ArrayStruct(a);
But I want it to be implicit. Or if there was a way to prevent assignments that would also work for me.
var a = new ArrayStruct();//let this work
var b = a; //Prevent this from happening
If you have any other solutions for replacing Lists with arrays conveniently please let me know
So do any of you have an idea of how I could make a new copy the array when I assign the struct to another variable? Of course, I know I could use a function to do something like so b = new ArrayStruct(a); But I want it to be implicit.
You can't do that. If a is a struct and you execute var b = a, then it will always just set b to a shallow copy of a; there's no way to change that behavior.
Doing something like b = new ArrayStruct(a) is the best you can do.
You're not understanding what the "=" operator is doing there. Objects are defined by reference, meaning when you write var b = a; you are actually asking for what you wish to avoid to happen, that is - object "b", pointing to the same object, which is "a".
You're passing the object "b" to point to the reference of object "a", which object holds the address to the object in question you defined, using the new keyword var a = new ArrayStruct<string>()
Your operator overloading does not work because you can't use it for conversions of the same type as the class you're writing the operator in. You'd either have to make a separate object (class), which would defeat the point you're trying to fix, or pass the m_array instead: public static implicit operator ArrayStruct<T>(T[] array)
You can also do something like this:
public static ArrayStruct<T> CopyFrom (ArrayStruct<T> array)
{
var newArray = new ArrayStruct<T>();
newArray.m_Array = new T[array.Count + 1];
newArray.Count = array.Count;
Array.Copy(array.m_Array, 0, newArray.m_Array, 0, array.m_Array.Length);
return newArray;
}
Now it should work exactly as you wanted.
var a = new ArrayStruct<string>();
a.Add("hello");
var b = ArrayStruct<string>.CopyFrom(a);
a.SetAt(0, "new value for a");
a.Add("resizing array");
Console.WriteLine(b.Count); // b returns 1
Console.WriteLine(a.Count); // a returns 2
As to why your index was going out of bounds, i.e it did not preserve the reference found in "a", this is all in correlation with references, yet again.
In the following code System.Array.Resize(ref m_Array, Count + 1); you're changing the reference found for object m_array in "a", when you're adding the new element with the line a.Add("resizing array");. That essentially means that your "a" object's m_array (which is another object of type array[]) is now pointing to a new object (of type array[]), which is of a new size.
Okay, but object "a" is now using the new reference, whilst object "b" is still using the old one. m_array in object "b" only has a single element, while the new m_array object in "a" has the newly added element "resizing array", along with any previously added ones.
I believe that's how System.Array.Resize works? Edit me if I'm wrong.
Below is a resource explaining how to do a deep cloning of an object, if that's what you truly wish to do (i.e create a completely new copy of an object, as well as any other objects inside)
Deep cloning of objects
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 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 have a variable called temp which is of type List<Point>. For some reason, when I call sanitize(temp), the variable temp seems to change. I know that the variable temp changes, because the output I get on C:\out.bmp is different if I include the sanitize(temp) line or not. The output I am getting from the messagebox is "temp was not changed."
List<Point> original = temp;
sanitize(temp);
if (temp.Equals(original)) {
MessageBox.Show(#"temp was not changed.");
} else {
MessageBox.Show(#"temp was changed.");
}
outputPointsOnBitmap(temp.ToArray(), ref windowBitmap, Color.Yellow);
windowBitmap.Save("C:\\out.bmp");
For the curious, here is more source code:
private void outputPointsOnBitmap(Point[] points, ref Bitmap bitmap, Color markerColor) {
foreach (Point point in points) {
bitmap.SetPixel(point.X, point.Y, markerColor);
}
}
private List<Point> sanitize(List<Point> crossPoints) {
SortedSet<int> indexesToDelete = new SortedSet<int>();
for (int i = 0; i < crossPoints.Count() - 1; i++) {
if (Math.Abs(crossPoints[i + 1].X - crossPoints[i].X) <= 5 &&
Math.Abs(crossPoints[i + 1].Y - crossPoints[i].Y) <= 5) {
indexesToDelete.Add(i);
indexesToDelete.Add(i + 1);
}
}
foreach (int i in indexesToDelete.Reverse()) {
crossPoints.RemoveAt(i);
}
return crossPoints;
}
This is driving me nuts.
When you do:
List<Point> original = temp;
You make the variable original refer to the list that temp refers to.
So any changes that sanitize makes to temp are also visible in original. temp.Equals(original) returns true because they are the same object. If you want original to be a new list with the same items as temp, construct a new List object:
List<Point> original = new List<Point>(temp);
You say you're not using references, but List<T> is a reference type because it doesn't inherit from ValueType - when you assign or pass it you're always using references.
Here's a good breakdown of the difference between value and reference types in .Net:
http://www.albahari.com/valuevsreftypes.aspx
As Jordy Langen points out, temp.Equals(original) will always return false now, because the the two lists are different objects (and that's what Object.Equals looks at for reference types). If you want to see whether the sanitize function changed the list at all, you can use Enumerable.SequenceEqual:
if (temp.SequenceEqual(original)) {
MessageBox.Show(#"temp was not changed.");
} else {
MessageBox.Show(#"temp was changed.");
}
If you want original to not change, do this:
List<Point> original = new List<Point>(temp);
sanitize(temp);
That will create a new List<Point> with the contents of temp. Otherwise, List<Point> original = temp just creates a new reference (original) to the same memory location which contains your list (temp).
In order to check if your 2 lists are different in your case, check their Count (since Equals will always return false).
if( temp.Count != original.Count )
{
// now you know that some items were removed in your sanitize method
}
temp changes because you're passing a reference to it into sanitize which is assigned to the crossPoints variable. Because it's passed by reference any changes to crossPoints (via the call to RemoveAt) also affect temp since they point to the same list.
When you say:
List<Point> original = temp;
You're making temp and original refence the same list. It does not create a copy of the list.
Temp and original are not objects, they are references to objects, in your case, they both reference the same list object. Normally, equals does reference comparison on objects, so it will say they are equal. Either way you are comparing a list object to itself. In your sanitize method, you change the same list.
It is being modified because it is being passed as a reference, so the delete operation is actually being performed on the same reference the temp variable holds. you may be the desired result if you do the following:
private List<Point> sanitize(List<Point> crossPoints) {
var workset = new List<Point>(crosspoints);
SortedSet<int> indexesToDelete = new SortedSet<int>();
for (int i = 0; i < workset.Count() - 1; i++) {
if (Math.Abs(workset[i + 1].X - workset[i].X) <= 5 &&
Math.Abs(workset[i + 1].Y - workset[i].Y) <= 5) {
indexesToDelete.Add(i);
indexesToDelete.Add(i + 1);
}
}
foreach (int i in indexesToDelete.Reverse()) {
workset.RemoveAt(i);
}
return workset;
}
of course keep in mind this is an O(n) operation since you are making a copy of the references in the list
This statement:
List<Point> original = temp;
original does now have the same reference as temp. Meaning: All changes you apply to the temp variable are also applied to the original variable because they share the same reference.
You should create a deep copy of the list to make this work. One more thing:
The Equals will always return false if you use a deep copy. Equals check if the references are equal. You most probable need to do check based on the items in the list.
Your list temp is an object, and objects are passed by reference, so your sanitize method is changing the underlying object.
However, your output says it's not changed because you assign original = temp, meaning that your variable original is a pointer to the same object as your variable temp, so when you compare the two they are equal, even though the object has actually changed since before it was passed to sanitize.
Is it possible to do something like this:
string A = "A";
string B = "B";
object[] O = { A, B };
O[0] = "C";
Where A will hold the value "C" in the end?
The code above will replace O[0] with "C", but A remains unchanged.
No - at least not in safe code.
When you create the array, it copies the values into the array. Changing the value in the array later won't change the value of the variable. There's no way of creating a sort of "ref array", where the array elements are shared with the variables. (As shown in Mark's answer, there are sometimes ways of doing this in unsafe code, but I'd really suggest staying away from that.)
Note, however, that if both the array and the variable refer to the same mutable object then mutating that object via either path will make a change which is visible via the other:
StringBuilder x = new StringBuilder();
StringBuilder[] array = new StringBuilder[] { x };
array[0].Append("Foo");
Console.WriteLine(x); // Prints "Foo"
This is no different to any other assignment though - and note that in the code above, the Append call doesn't change the value of either x or the array element; it changes the data within the object that those values refer to.
While the answer is no in the case of string, you can do this with value types:
class Program
{
static unsafe void Main()
{
char A = 'A';
char B = 'B';
var O = new char*[] { &A, &B };
*O[0] = 'C';
System.Console.WriteLine(A + "," + B); // outputs C,B
}
}
Unsafe code is typically frowned upon in C#. So, while this kind of thing is possible, I wouldn't recommend doing it.
What you probably want is to convert your strings into objects with properties.
You can then Cast your array object on the specific key to this object type you created, and then you can set its property again.
This way you can change both what's in your array, as your original variable, therefor, it is similar to seeing it as an array with values by reference.
public class stringObject
{
private string name;
public string Name { get { return name; } set { name = value; } }
}
stringObject A = new stringObject();
A.Name = "A";
stringObject B = new stringObject();
B.Name = "B";
object[] O = { A, B };
//change the array at key 0, and also changing our original variable
stringObject C = O[0] as stringObject;
C.Name = "C";
The above code will not only change what is inside O[0], but it will also update what is inside your object A.
An example with a test to write to the console can be seen here:
https://dotnetfiddle.net/Yt25hy
I'm about ready to bang my head against the wall
I have a class called Map which has a dictionary called tiles.
class Map
{
public Dictionary<Location, Tile> tiles = new Dictionary<Location, Tile>();
public Size mapSize;
public Map(Size size)
{
this.mapSize = size;
}
//etc...
I fill this dictionary temporarily to test some things..
public void FillTemp(Dictionary<int, Item> itemInfo)
{
Random r = new Random();
for(int i =0; i < mapSize.Width; i++)
{
for(int j=0; j<mapSize.Height; j++)
{
Location temp = new Location(i, j, 0);
int rint = r.Next(0, (itemInfo.Count - 1));
Tile t = new Tile(new Item(rint, rint));
tiles[temp] = t;
}
}
}
and in my main program code
Map m = new Map(10, 10);
m.FillTemp(iInfo);
Tile t = m.GetTile(new Location(2, 2, 0)); //The problem line
now, if I add a breakpoint in my code, I can clearly see that my instance (m) of the map class is filled with pairs via the function above, but when I try to access a value with the GetTile function:
public Tile GetTile(Location location)
{
if(this.tiles.ContainsKey(location))
{
return this.tiles[location];
}
else
{
return null;
}
}
it ALWAYS returns null. Again, if I view inside the Map object and find the Location key where x=2,y=2,z=0 , I clearly see the value being a Tile that FillTemp generated..
Why is it doing this? I've had no problems with a Dictionary such as this so far. I have no idea why it's returning null. and again, when debugging, I can CLEARLY see that the Map instance contains the Location key it says it does not...
very frustrating.
Any clues? Need any more info?
Help would be greatly appreciated :)
You don't show what 'Location' is but this is normal behavior if it is a class: objects are tested for Equality by comparing the references. So different instances of Location will always be unequal, even if their content is the same.
The quickest fix is to override Equals() and GetHashCode() for the Location class. And then it is a good idea to (re)design it as an immutable class (or maybe immutable struct).
Henk is correct; when you test to see if two objects are equal in .Net, you're actually asking if "reference x is pointing to the same object as reference y".
So, by default:
Location a = new Location(2, 2, 0);
Location b = new Location(2, 2, 0);
Location c = a;
bool notEqual = ( a == b ); // false
bool equal = ( a == c ); // true
To get around this, you need to override the equality methods for your Location object to compare the values for equality - the body of your Equals method, for example, might end us as something like:
return (this.x == that.x && this.y == that.y && this.z == that.z);
Thank you everyone! I really appreciate it!
I made Location into a value type and now it's working just fine.
I'm sorry for not posting the whole code, but I didn't feel it was necessary and assumed that the reader could assume that location was a simple class with x,y,z int values.
I learned many new things because of all of you and my understanding of this (generally) wonderful language is greater because of it :o)
Take care,