user3010006
user3010006

Reputation: 13

List.Contains not working as expected with custom struct argument

I have a struct IntVector2 which has an X and Y property. The + operator is overridden with

public static IntVector2 operator +(IntVector2 value1, IntVector2 value2)
{
    value1.X += value2.X;
    value1.Y += value2.Y;
    return value1;
}

When using this in a List with the contains method, it's not checking the total value of the addition, but just the variable "current"

if (visited.Contains(current + dir))
    continue;

What's actually going on here?

Edit: Here's a screenshot of the values of the variables, and a variable declaration that equals what I'm expecting the value of the contains to check.

https://dl.dropboxusercontent.com/u/30062610/Brokestuff.png

Edit2: Here's the full code of the method, it's the start of an A* Pathfinding algorithm finding the end vector from the start vector.

                    public Path Pathfind(IntVector2 start, IntVector2 end)
    {
        Queue<IntVector2> fillQueue = new Queue<IntVector2>();
        List<IntVector2> visited = new List<IntVector2>();
        fillQueue.Enqueue(start);
        IntVector2 current;
        while (fillQueue.Count > 0)
        {
            current = fillQueue.Dequeue();
            foreach (IntVector2 dir in Directions)
            {
                if (GetCell(current + dir).IsWall)
                    continue;
                else
                {
                    IntVector2 newstuff = current + dir;
                    if (visited.Contains(current + dir))
                        continue;
                    if ((current + dir) == end)
                    {
                        //We've reached the target, traceback the path and return it.
                    }
                    visited.Add(current);
                    fillQueue.Enqueue(current + dir);
                }
            }
        }
        return null;
    }

Edit 3: Even using the newstuff variable which has a different value from the start hits the continue. I'm not sure what it could be doing. My equals override just checks if X and Y are both equal and returns true if so.

Here's the entirety of the IntVector2 code: http://pastebin.com/ic108SeF

Edit 4: I modified the + operator to:

        public static IntVector2 operator +(IntVector2 value1, IntVector2 value2)
    {
        return new IntVector2((value1.X + value2.X), (value1.Y + value2.Y));
    }

And the problem still persists.

Upvotes: 1

Views: 697

Answers (3)

Ryan Mann
Ryan Mann

Reputation: 5357

Ok, I believe I figured out your problem.

Your equals override isn't an override, you have it in your code as:

    public bool Equals(IntVector2 other) { 
        return (X == other.X) && (Y == other.Y); 
    }

What you did there was added a Method called Equals. So you have in effect overloaded the Actual equals method you need to override. Contains won't call your equals method, because it calls the original one that takes an object.

And when you override the right equals method you should, in good practice, implement GetHashCode and use GetHashCode to determine if the objects are truly equal.

In your case you won't have an issue not overriding GetHashCode, as you are basing eqaulity on two integers being the same in another copy of the IntVector2, and you can't really compute an integer hash code for that as X and Y are both integers. If you did a GetHashCode implementation here, you could run into bugs later if you have a large number of these you could end up with dupe hash codes that are non equal objects.

Here is the updated code you should try.

public struct IntVector2
{
    public int X { get; set; }
    public int Y { get; set; }

    public static IntVector2 operator +(IntVector2 value1, IntVector2 value2)
    {
        value1.X += value2.X;
        value1.Y += value2.Y;
        return value1;
    }

    public override int GetHashCode()
    {
        //overrode this to get rid of warning
        return base.GetHashCode();
    }

    //This equals get's called, notice the override keyword
    public override bool Equals(object obj)
    {
        if (obj is IntVector2)
        {
            IntVector2 vObj = (IntVector2)obj;
            return vObj.X == this.X && vObj.Y == this.Y;           
        }
        return false;
    }

    //This won't get called, it's not part of the framework, this is adding a new overload for equals that .Net won't know about.
    public bool Equals(IntVector2 other)
    {
        return (X == other.X) && (Y == other.Y);
    }

    public override string ToString()
    {
        return string.Format("{ value1: {0}, value2: {0} }", X, Y);
    }
}

Upvotes: 1

Michael Liu
Michael Liu

Reputation: 55489

You have a typo in the Equals method:

public override bool Equals(object obj)
{ 
    if (obj is IntVector2)
    {
        return Equals((IntVector2)this); // <-- "this" should be "obj"
    }
}

The erroneous code compares this to this, so it always returns true.

Upvotes: 1

Sean O&#39;Brien
Sean O&#39;Brien

Reputation: 183

With your current code, Contains is unable to determine equality for two structure instances. If you override IntVector2::GetHashCode so that identical values return the same hash, it should start working as you expect.

Upvotes: -1

Related Questions