2

I am trying to create a check between lists, but am having no luck :-/

I have a game board with 100 fields and make this loop to add only empty fields into a new list:

for(int i = 0; i < thisGame.boardFields.Count; i++)
{
    if (thisGame.boardFields.Count != 0 && thisGame.boardFields [i] != null) 
    {
        BoardField thisField = thisGame.boardFields [i];
        if (thisField.owner == "0" && thisField.number != 13) 
        {
            Tile tTile = new Tile();
            tTile.color = thisField.color;
            tTile.number = thisField.number.ToString();

            tempBoard.Add (tTile);
        }
    }
}

Then I loop through the players 5 tiles to see if the player has a tile which is not playable, e.g. a empty field with the same object is not available like this:

for (var i = 0; i < thisGame.playerTiles.Count; i++)
{    
    Tile tempTile = new Tile();
    tempTile.color = thisGame.playerTiles[i].color;
    tempTile.number = thisGame.playerTiles[i].number.ToString();

    if (!tempBoard.Contains (tempTile)) 
    {
        testTile = tempTile;
        print ("HUSTON WE HAVE A PROBLEM: " + tempTile.color + "-" + tempTile.number);
    }    
}

This is the definition of the Tile class:

public class Tile 
{    
    public int id;
    public string color;
    public string number;   
}

Now my problem is, that it prints on every 5 tiles in the players tile list? All tiles in the players tile list is available in the tempBoard list?

What am I missing her?

Hoping for help and thanks in advance :-)

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
Mansa
  • 2,277
  • 10
  • 37
  • 67

2 Answers2

8

To work Contains needs to check equality between two objects. By default, for reference types it does it by checking reference equality. Therefore, having two different instances (objects in memory) of same type and with same values to all properties will still result in them being not equal, and for Contains to return false.

To overcome this 3 options:

  1. you need your class to override the Equals and GetHashCode methods of object. References on overriding the methods:

  2. Another option instead of overriding the methods in your class is to create a class implementing the IEquatable<Tile> interface and then use the overload:

    list.Contains(searchedTile, new SomeClassImplementingInterface());
    

    Id use this in case the overriding of the Equals/GetHashCode are non-trivial (do not include all properties of the class) or if you do not have control over the Tile class.

  3. Use linq's Any method:

    collection.Any(item => item.number == tempTile.number && item.color == temTile.color);
    

Also, it is nice to initialize your objects using the object initializer syntax:

Tile tTile = new Tile
{ 
    color = thisField.color,
    number = thisField.number.ToString()
}

And if you have your fields public they should probably be defined as properties:

public class Tile 
{
    public int Id { get; set; }
    public string Color { get; set; }
    public string Number { get; set; }
}

About properties vs fields:

And last have a look at naming conventions for C#

Gilad Green
  • 36,708
  • 7
  • 61
  • 95
  • Trying to figure this out, what is: new TileEquatable()? – Mansa Oct 01 '17 at 18:26
  • Sorry for not understanding. What is that class? Whats in it? – Mansa Oct 01 '17 at 18:37
  • @Mansa - updated answer to make a bit more clear. Basically it is a class with the implementation of the interface – Gilad Green Oct 01 '17 at 18:40
  • This is way over my head :-/ I thought I had my shit together, but I simply do not understand that part, but thanks for trying ;-) – Mansa Oct 01 '17 at 18:44
  • @Mansa - you do not have to use the interface. The first part explains about the overriding. That is probably the correct way to go – Gilad Green Oct 01 '17 at 18:45
  • @Mansa - see update clarifying the different options and additional one. I still think going for option 1 is the correct way and in terms of how advanced the least - so good to start with – Gilad Green Oct 01 '17 at 18:47
  • Many thanks for your explanation. I will go with the collection.Any() for now but look deeper into the override method. – Mansa Oct 01 '17 at 19:08
0

According to this Stack Overflow answer (https://stackoverflow.com/a/9264597/1102726), if you don't provide an overide for IEquatable. Equals, Contains will do a reference comparison. Since you had just instantiated tempTile, it will never be in that list. You can either provide that override in your definition of Tile or change out the Contains(tempTile) for something like tempBoard.Any(t => t.number == tempTile.number) or however you define two tiles being the same.

Becca Dee
  • 1,530
  • 1
  • 24
  • 51
  • Is it possible to add 2 fields to: tempBoard.Any(t => t.number == tempTile.number)? I need to check if the object has both the number and color? – Mansa Oct 01 '17 at 18:40
  • You can do anything you'd like, so long as it evaluates to a boolean true/false value. You would then do `t.number == tempTile.number && t.color == tempTile.color`. – Becca Dee Oct 02 '17 at 00:35
  • `Any` the way I'm suggesting basically says, "let's go through every item in the collection, let's call the item `t`, let's check to see if some boolean expression involving `t` evaluates to `true`, and if so, we'll say `Any` returns `true`. – Becca Dee Oct 02 '17 at 00:41
  • Oh, and to clarify, you'll have to have `using System.Linq;` – Becca Dee Oct 02 '17 at 00:45