c#List <t> .Contains()方法返回False </t>

时间:2012-10-11 15:40:25

标签: c# asp.net

在下面的代码块中,我希望dictCars包含: {雪佛兰:卡马罗,道奇:充电器}

但是,dictCars回来了。因为每次调用此行时返回false:

if(myCars.Contains(new Car(Convert.ToInt64(strCar.Split(':')[1]),strCar.Split(':')[2])))

代码块:

public class Car
{
    public long CarID { get; set; }

    public string CarName { get; set; }

    public Car(long CarID, string CarName)
    {
        this.CarID = CarID;
        this.CarName = CarName;
    }
}

List<Car> myCars = new List<Car>();
myCars.Add(new Car(0,"Pinto"));
myCars.Add(new Car(2,"Camaro"));
myCars.Add(new Car(3,"Charger"));

Dictionary<string, string> dictCars = new Dictionary<string, string>();
string strCars = "Ford:1:Mustang,Chevy:2:Camaro,Dodge:3:Charger";
String[] arrCars = strCars.Split(',');
foreach (string strCar in arrCars)
{
    if(myCars.Contains(new Car(Convert.ToInt64(strCar.Split(':')[1]),strCar.Split(':')[2])))
    {
        if (!dictCars.ContainsKey(strCar.Split(':')[0]))
        {
            dictCars.Add(strCar.Split(':')[0], strCar.Split(':')[2]);
        }
    }
}

return dictCars;

问题:我的List.Contains实施有什么问题?

提前致谢!

9 个答案:

答案 0 :(得分:7)

你需要告诉包含使两个Car相等的内容。默认情况下,它将使用ReferenceEquals,如果它们是同一个实例,它们只会调用两个等于的对象。

覆盖Equals课程中的GetHashCodeCar,或定义IEqualityComparer<Car>课程并将其传递给Contains

如果两个具有相同Car的{​​{1}}“相等”,那么实现非常简单:

CarID

答案 1 :(得分:5)

您的Car班级是参考类型。默认情况下,引用类型通过引用相互比较,这意味着如果它们在内存中引用相同的实例,则它们被认为是相同的。在您的情况下,如果它们包含相同的值,则希望它们被视为相等。

要更改平等行为,您需要覆盖EqualsGetHashCode

如果两辆车只有在IDName相等时才相等,以下是平等成员的一种可能实施方式:

protected bool Equals(Car other)
{
    return CarID == other.CarID && string.Equals(CarName, other.CarName);
}

public override bool Equals(object obj)
{
    if (ReferenceEquals(null, obj))
        return false;
    if (ReferenceEquals(this, obj))
        return true;
    var other = obj as Car;
    return other != null && Equals(other);
}

public override int GetHashCode()
{
    unchecked
    {
        return (CarID.GetHashCode() * 397) ^ 
                (CarName != null ? CarName.GetHashCode() : 0);
    }
}

此实施由ReSharper自动创建 它考虑了null值和Car子类的可能性。此外,它提供了GetHashCode的有用实现。

答案 2 :(得分:2)

您假设具有相同CarID和CarName的两个Car实例相等。

这是不正确的。默认情况下,每个new Car(...)与其他汽车的不同,因为它们是对不同对象的引用。

有几种方法可以“修复”:

  • 为您的汽车使用struct而不是class

    Structs继承ValueType的默认实现Equals,它会比较所有字段和属性以确定相等。

    请注意,在这种情况下,建议您将Car结构不可变为avoid common problems with mutable structs

  • <强> Override Equals and GetHashCode

    这样,List.Contains就会知道您打算使用相同ID和名称的汽车。

  • 使用其他方法代替List.Contains

    例如,Enumerable.Any允许您指定可匹配的谓词:

    bool exists = myCars.Any(car => car.ID == Convert.ToInt64(strCar.Split(':')[1])
                                    && car.Name = strCar.Split(':')[2]);
    

答案 3 :(得分:2)

您可以通过实施IEquatable

来添加此代码
public class Car: IEquatable<Car>
{

    ......

    public bool Equals( Car other )
    {
        return this.CarID  == other.CarID && this.CarName == other.CarName;
    }
}

链接:http://msdn.microsoft.com/fr-fr/library/vstudio/ms131187.aspx

答案 4 :(得分:1)

您需要实施Equals。最有可能是:

public override bool Equals(object obj)
{
     Car car = obj as Car;
     if(car == null) return false;
     return car.CarID == this.CarID && car.CarName == this.CarName;
}

答案 5 :(得分:0)

您的汽车类需要实现IEquatable接口并定义Equals方法,否则contains方法正在比较基础引用。

答案 6 :(得分:0)

集合永远不会“包含”使用默认new比较的新Object.Equals ed对象。 (默认比较为ReferenceEquals,只是比较实例。将现有Carnew Car()进行比较

永远不会成真

要以这种方式使用Contains,您需要:

  1. 覆盖Car.Equals(和Car.GetHashCode)以指定相同的含义,或

  2. 实施IEqualityComparer<Car>比较实例,并在调用Contains时指定。

  3. 请注意第一个选项中的副作用,Car.Equals(Car)的其他用法也会使用此比较。


    否则,您可以使用Any并自行指定比较(但恕我直言,这有点气味 - 汽车应该知道如何比较自己):

    if(myCars.Any(c=> c.CarID == Convert.ToInt64(strCar.Split(':')[1]) && c.CarName == strCar.Split(':')[2]))
    

答案 7 :(得分:0)

您需要实现IEqualityComparer

有关如何操作的更多信息,请点击此处; http://msdn.microsoft.com/en-us/library/bb339118.aspx

// Custom comparer for the class 
 class CarComparer : IEqualityComparer<Car>
{
// Products are equal if their names and product numbers are equal. 
public bool Equals(Car x, Car y)
{

    //Check whether the compared objects reference the same data. 
    if (Object.ReferenceEquals(x, y)) return true;

    //Check whether any of the compared objects is null. 
    if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null))
        return false;

    //Check whether the properties are equal. 
    return x.CarID == y.CarID && x.CarName == y.CarName;
}

// If Equals() returns true for a pair of objects  
// then GetHashCode() must return the same value for these objects. 

public int GetHashCode(Car car)
{
    //Check whether the object is null 
    if (Object.ReferenceEquals(car, null)) return 0;

    //Get hash code for the Name field if it is not null. 
    string hashCarName = car.CarName == null ? 0 : car.CarName.GetHashCode();

    //Get hash code for the ID field. 
    int hashCarID = car.CarID.GetHashCode();

    //Calculate the hash code for the product. 
    return hashCarName ^ hashCarID;
}

检查是否相等;

CarComparer carComp = new CarComparer();
bool blnIsEqual = CarList1.Contains(CarList2, carComp);

答案 8 :(得分:-1)

myCars.Contains(newCar)
myCars.Where(c =>  c.CarID == newCar.CarID && c.CarName==newCar.CarName).Count() > 0