.Contains() on a list of custom class objects
Asked Answered
J

7

118

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?

Julianejuliann answered 13/4, 2010 at 11:34 Comment(0)
C
137

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;
    }
}
Chiaroscuro answered 13/4, 2010 at 11:38 Comment(4)
but where is GetHashCode()?Boorman
You don't need to implement GetHashCode(). It works without it.Aekerly
This doesn't seem to work with the .Contains method: dotnetfiddle.net/OQwyyWSafelight
@MichaelMurphy you forget public class M : IEquatable<M>Photic
M
166

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.

Muscid answered 13/4, 2010 at 12:4 Comment(0)
C
137

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;
    }
}
Chiaroscuro answered 13/4, 2010 at 11:38 Comment(4)
but where is GetHashCode()?Boorman
You don't need to implement GetHashCode(). It works without it.Aekerly
This doesn't seem to work with the .Contains method: dotnetfiddle.net/OQwyyWSafelight
@MichaelMurphy you forget public class M : IEquatable<M>Photic
L
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...

Leventhal answered 13/4, 2010 at 11:39 Comment(3)
how can Linq ever be overkill?Floatage
@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...Leventhal
+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)Chiaroscuro
T
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.)

Thomasthomasa answered 13/4, 2010 at 11:37 Comment(11)
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...Leventhal
Do you hvave some examples of this, .Find() or overriding the Object.Equals/GetHashCode?Julianejuliann
@Martin IT would be very broken if you wanted the comparison of two CartProduct objects to behave differently in different places.Chiaroscuro
@Martin: Yes, it will be used whenever something tries to compare instances of your type. (If you want just a local effect use List<T>.Exists() passing in a method that does the comparison.)Thomasthomasa
@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...Leventhal
@Thomasthomasa - 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.Leventhal
@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.Chiaroscuro
@Martin: List.Contains determines existence using an object, List.Exists determines the same thing with a predicate.Thomasthomasa
@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...Leventhal
@Thomasthomasa - True - but I think the original poster wants to actually retrieve the object, not merely test if it exists...Leventhal
@Thomasthomasa - Actually - I see where you're coming from now. Either one of us could be right lol...Leventhal
F
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);
}
Fichtean answered 7/5, 2020 at 9:0 Comment(0)
D
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))
Dissipate answered 24/2, 2019 at 10:56 Comment(0)
L
-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).

Lysander answered 13/4, 2010 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.