115

I'm trying to use the .Contains() function on a list of custom objects.

This is the list:

List<CartProduct> CartProducts = new List<CartProduct>();

And the CartProduct:

public class CartProduct
{
    public Int32 ID;
    public String Name;
    public Int32 Number;
    public Decimal CurrentPrice;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="ID">The ID of the product</param>
    /// <param name="Name">The name of the product</param>
    /// <param name="Number">The total number of that product</param>
    /// <param name="CurrentPrice">The currentprice for the product (1 piece)</param>
    public CartProduct(Int32 ID, String Name, Int32 Number, Decimal CurrentPrice)
    {
        this.ID = ID;
        this.Name = Name;
        this.Number = Number;
        this.CurrentPrice = CurrentPrice;
    }
    public String ToString()
    {
        return Name;
    }
}

When I try to find a similar cartproduct within the list:

if (CartProducts.Contains(p))

it ignores similar cartproducts and I don't seem to know what it checks on - the ID? or at all?

cottontail
  • 10,268
  • 18
  • 50
  • 51
Jan Johansen
  • 1,999
  • 6
  • 30
  • 43

7 Answers7

163

If you are using .NET 3.5 or newer you can use LINQ extension methods to achieve a "contains" check with the Any extension method:

if(CartProducts.Any(prod => prod.ID == p.ID))

This will check for the existence of a product within CartProducts which has an ID matching the ID of p. You can put any boolean expression after the => to perform the check on.

This also has the benefit of working for LINQ-to-SQL queries as well as in-memory queries, where Contains doesn't.

Paul Turner
  • 38,949
  • 15
  • 102
  • 166
133

You need to implement IEquatable or override Equals() and GetHashCode()

For example:

public class CartProduct : IEquatable<CartProduct>
{
    public Int32 ID;
    public String Name;
    public Int32 Number;
    public Decimal CurrentPrice;

    public CartProduct(Int32 ID, String Name, Int32 Number, Decimal CurrentPrice)
    {
        this.ID = ID;
        this.Name = Name;
        this.Number = Number;
        this.CurrentPrice = CurrentPrice;
    }

    public String ToString()
    {
        return Name;
    }

    public bool Equals( CartProduct other )
    {
        // Would still want to check for null etc. first.
        return this.ID == other.ID && 
               this.Name == other.Name && 
               this.Number == other.Number && 
               this.CurrentPrice == other.CurrentPrice;
    }
}
Rowland Shaw
  • 37,700
  • 14
  • 97
  • 166
14

It checks to see whether the specific object is contained in the list.

You might be better using the Find method on the list.

Here's an example

List<CartProduct> lst = new List<CartProduct>();

CartProduct objBeer;
objBeer = lst.Find(x => (x.Name == "Beer"));

Hope that helps

You should also look at LinQ - overkill for this perhaps, but a useful tool nonetheless...

Martin Milan
  • 6,346
  • 2
  • 32
  • 44
  • 1
    how can Linq ever be overkill? – Mel Gerats Apr 13 '10 at 12:25
  • @MEL - Why get mixed up in a query and type inference for something this simple? That said though, it might be more readable to someone not familiar with lamdas... – Martin Milan Apr 13 '10 at 13:15
  • +1 Good clear example, that shows the option that wouldn't be affected by changes elsewhere (i.e. if the `Equals()` method got changed for whatever reason) – Rowland Shaw Apr 13 '10 at 15:18
5

By default reference types have reference equality (i.e. two instances are only equal if they are the same object).

You need to override Object.Equals (and Object.GetHashCode to match) to implement your own equality. (And it is then good practice to implement an equality, ==, operator.)

Richard
  • 106,783
  • 21
  • 203
  • 265
  • 1
    Why override Object.Equals, which could have consequences elsewhere in the code? To me, it makes more sense to amend the search code accordingly, and not the underlying class of object being searched upon... – Martin Milan Apr 13 '10 at 11:45
  • Do you hvave some examples of this, .Find() or overriding the Object.Equals/GetHashCode? – Jan Johansen Apr 13 '10 at 11:49
  • @Martin IT would be very broken if you wanted the comparison of two `CartProduct` objects to behave differently in different places. – Rowland Shaw Apr 13 '10 at 11:51
  • @Martin: Yes, it will be used whenever something tries to compare instances of your type. (If you want just a local effect use `List.Exists()` passing in a method that does the comparison.) – Richard Apr 13 '10 at 11:52
  • 1
    @Rowland - But I'm not saying he would have to change how a comparison works. If he wants a specific object, use Contains(). If he wants any object matching a specified criteria, use Find() with a suitable predicate (lamda expression)... I'm actually arguing that you don't touch the comparison code AT ALL - you just call the right method on the list for the task you're trying to accomplish... – Martin Milan Apr 13 '10 at 12:01
  • @Richard - Exists() just tells you that there is a suitable object there. If you want to return that object, you'll need Find() - with a suitable predicate as you have suggested. – Martin Milan Apr 13 '10 at 12:03
  • 1
    @Martin Appears I misinterpreted your comment to be something along the lines of "override `Contains()`". Agree that `Find()` could solve the issue, although I would suggest having a suitable equals method may be more useful in loads of other cases, as the OP didn't spot that the references for two instances of the same entity were different. – Rowland Shaw Apr 13 '10 at 12:06
  • @Martin: `List.Contains` determines existence using an object, `List.Exists` determines the same thing with a predicate. – Richard Apr 13 '10 at 13:04
  • @Rowland It's horses for courses - but I would be reluctant to make such a fundamental change to a class of objects just to enable searching like this. You're right, it's one way of doing it - just not the way I would have gone myself... – Martin Milan Apr 13 '10 at 13:10
  • @Richard - True - but I think the original poster wants to actually retrieve the object, not merely test if it exists... – Martin Milan Apr 13 '10 at 13:11
  • @Richard - Actually - I see where you're coming from now. Either one of us could be right lol... – Martin Milan Apr 13 '10 at 13:13
1

You need to create a object from your list like:

List<CartProduct> lst = new List<CartProduct>();

CartProduct obj = lst.Find(x => (x.Name == "product name"));

That object get the looked value searching by their properties: x.name

Then you can use List methods like Contains or Remove

if (lst.Contains(obj))
{
   lst.Remove(obj);
}
0

Implement override Equals() and GetHashCode()

public class CartProduct
{
    public Int32 ID;
    ...

    public CartProduct(Int32 ID, ...)
    {
        this.ID = ID;
        ...
    }

    public override int GetHashCode()
    {
        return ID;
    }

    public override bool Equals(Object obj)
        {
            if (obj == null || !(obj is CartProduct))
                return false;
            else
                return GetHashCode() == ((CartProduct)obj).GetHashCode();
        }

}

used:

if (CartProducts.Contains(p))
A. Morel
  • 9,210
  • 4
  • 56
  • 45
-2

If you want to have control over this you need to implement the [IEquatable interface][1]

[1]: http://This method determines equality by using the default equality comparer, as defined by the object's implementation of the IEquatable.Equals method for T (the type of values in the list).

Gerrie Schenck
  • 22,148
  • 20
  • 68
  • 95