0

以下のコード ブロックでは、dictCars に { Chevy:Camaro, Dodge:Charger } が含まれていると予想されます。

しかし、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 の実装で何が間違っていますか?

前もって感謝します!

4

9 に答える 9

7

2 つの値が等しくなるものを Contains に伝える必要がありますCar。デフォルトでは、 ReferenceEquals2 つのオブジェクトが同じインスタンスである場合にのみ、これを呼び出します。

クラスでオーバーライドするかEquals、クラスを定義してそれを に渡します。GetHashCodeCarIEqualityComparer<Car>Contains

同じ値を持つ2 つCarの sCarIDが「等しい」場合、実装は非常に簡単です。

public override bool Equals(object o)
{
   if(o.GetType() != typeof(Car))
     return false;

   return (this.CarID == ((Car)o).CarID);
}

public override int GetHashCode()
{
  return CarID.GetHashCode();
}
于 2012-10-11T15:44:16.803 に答える
6

あなたのCarクラスは参照型です。デフォルトでは、参照型は参照によって相互に比較されます。つまり、メモリ内の同じインスタンスを参照する場合、それらは同じと見なされます。あなたの場合、同じ値が含まれている場合、それらは等しいと見なされます。

等価動作を変更するには、 と をオーバーライドする必要がありEqualsますGetHashCode

IDとが等しい場合にのみ 2 つの車が等しい場合Name、等値メンバの可能な実装の 1 つを次に示します。

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 によって自動的に作成されています。値と のサブクラスの可能性が
考慮されます。さらに、 の便利な実装を提供します。nullCarGetHashCode

于 2012-10-11T15:43:17.747 に答える
2

同じ CarID と CarName を持つ 2 つの Car インスタンスが等しいと想定しています。

これは正しくありません。デフォルトでは、それぞれnew Car(...)異なるオブジェクトへの参照であるため、それぞれが他の車とは異なります。

それを「修正」するには、いくつかの方法があります。

  • 車には a のstruct代わりに aを使用してください。class

    構造体は、すべてのフィールドとプロパティを比較して等しいかどうかを判断するValueTypeのデフォルトの実装を継承します。Equals

    この場合、変更可能な構造体に関する一般的な問題を回避Carするために、構造体を不変にすることをお勧めします。

  • Equals と GetHashCode をオーバーライドします。

    そうList.Containsすれば、同じ ID と名前を持つ Car を同じにするつもりであることがわかります。

  • の代わりに別の方法を使用してくださいList.Contains

    たとえば、Enumerable.Any一致できる述語を指定できます。

    bool exists = myCars.Any(car => car.ID == Convert.ToInt64(strCar.Split(':')[1])
                                    && car.Name = strCar.Split(':')[2]);
    
于 2012-10-11T15:45:07.300 に答える
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

于 2012-10-11T15:46:20.437 に答える
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;
}
于 2012-10-11T15:46:47.890 に答える
0

車のクラスは、インターフェイス IEquatable を実装し、Equals メソッドを定義する必要があります。そうしないと、contains メソッドが基になる参照を比較します。

于 2012-10-11T15:43:31.887 に答える
0

newコレクションは、デフォルトのObject.Equals比較を使用する新しく編集されたオブジェクトを「含む」ことはできません。(デフォルトの比較はReferenceEquals、単純にインスタンスを比較する です。これは、既存のCarと を比較すると真になることはありませんnew Car())

この方法で使用するContainsには、次のいずれかが必要です。

  1. Car.Equals(and ) をオーバーライドCar.GetHashCodeして、同等であるとはどういう意味かを指定するか、または

  2. IEqualityComparer<Car>インスタンスを比較する を実装し、への呼び出しでそれを指定しますContains

Car.Equals(Car)最初のオプションでは、他の用途でもこの比較が使用されるという副作用に注意してください。


それ以外の場合は、Any自分で比較を使用して指定できます (ただし、これは少し変な匂いがします。車は自分自身を比較する方法を知っている必要があります)。

if(myCars.Any(c=> c.CarID == Convert.ToInt64(strCar.Split(':')[1]) && c.CarName == strCar.Split(':')[2]))
于 2012-10-11T15:44:00.007 に答える
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);
于 2012-10-11T15:44:42.477 に答える