views:

81

answers:

3

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

+7  A: 

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 Holterman
Aha, I get understand that completely.I already had an override of Equals, but I'll look into doing the same for GetHashCode and possibly == and != .. Also, I'll look into immutability.. thanks much
C Patton
@C Patton: If you'd already overridden Equals, you should have received a warning about not overriding GetHashCode. Always check the warnings :)
Jon Skeet
+1  A: 

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);
Dexter
Yes, but do use a guideline. Several members must be overridden consistently.
Henk Holterman
You don't *override* operators, you *overload* them - and you don't actually need to overload == for a type to work in a dictionary for references to equal but distinct objects. You need to override Equals and GetHashCode, which isn't the same thing. It's quite possible to override Equals/GetHashCode without overloading any operators.
Jon Skeet
Note that the Microsoft guidelines recommend avoiding overloading equality operators on mutable reference types. You could make Location a value type. You'll also want to overload/override !=, Equals, and GetHashCode, and probably implement IEquatable of T. Which is why I try to avoid going down this road.
TrueWill
@Jon Skeet - oops, that's embarrassing. I've fixed it now (although in my [poor] defence, the article I linked to is also similarly incorrect).@Henk Holterman and @TrueWill - these are good points. The full list of methods that you would need to consider overloading are included in the answer that I linked to, but the OP should also consider making the type immutable or a value type.
Dexter
A: 

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,

C Patton