为什么没有List.Contains按照我的预期工作?

时间:2014-10-20 18:36:22

标签: c#

为什么这个程序打印“未添加”而我认为它应该打印“添加”?

using System;
using System.Collections.Generic;

class Element
{
    public int id;

    public Element(int id)
    {
        this.id = id;
    }

    public static implicit operator Element(int d)  
    {
        Element ret = new Element(d);
        return ret;
    }

    public static bool operator ==(Element e1, Element e2)
    {
        return (e1.id == e2.id);
    }

    public static bool operator !=(Element e1, Element e2)
    {
        return !(e1.id == e2.id);
    }
}

class MainClass
{
    public static void Main(string[] args)
    {
        List<Element> element = new List<Element>();
        element.Add(2);
        if(element.Contains(2))
            Console.WriteLine("added");
        else
            Console.WriteLine("not added");
    }
}

Contains方法不使用==运算符。有什么问题?

3 个答案:

答案 0 :(得分:57)

  

Contains方法不使用==运算符

否 - 它使用Equals,您没有覆盖...所以您将获得Equals的默认行为,即检查引用标识。您应该覆盖Equals(object)GetHashCode以使其彼此保持一致 - 为了理智,与您的==重载一致。

我还建议实施IEquatable<Element>List<Element>将优先使用Equals(object)EqualityComparer<T>.Default正确地选择它。

哦,你的运算符重载也应该处理空引用。

我也强烈建议使用私有字段而不是公共字段,并使您的类型不可变 - 密封它并使id只读。实现可变类型的相等可能导致奇怪的情况。例如:

Dictionary<Element, string> dictionary = new Dictionary<Element, string>();
Element x = new Element(10);
dictionary[x] = "foo";
x.id = 100;
Console.WriteLine(dictionary[x]); // No such element!

这会发生,因为哈希代码会改变(至少在大多数实现中),因此字典下的哈希表甚至无法找到对已经存在的相同对象的引用在那里。

所以你的课看起来像这样:

internal sealed class Element : IEquatable<Element>
{
    private readonly int id;

    public int Id { get { return id; } }

    public Element(int id)
    {
        this.id = id;
    }

    public static implicit operator Element(int d)  
    {
        return new Element(d);
    }

    public static bool operator ==(Element e1, Element e2)
    {
        if (object.ReferenceEquals(e1, e2))
        {
            return true; 
        }
        if (object.ReferenceEquals(e1, null) ||
            object.ReferenceEquals(e2, null))
        {
            return false; 
        }
        return e1.id == e2.id;
    }

    public static bool operator !=(Element e1, Element e2)
    {
        // Delegate...
        return !(e1 == e2);
    }

    public bool Equals(Element other)
    {
        return this == other;
    }

    public override int GetHashCode()
    {
        return id;
    }

    public override bool Equals(object obj)
    {
        // Delegate...
        return Equals(obj as Element);
    }
}

(顺便说一句,我不确定隐式转换的优点 - 我通常会远离那些,我自己。)

答案 1 :(得分:12)

  

Contains方法不使用==运算符。有什么问题?

这是正确的。

  

此方法[Contains]通过使用默认的相等比较器来确定相等性,该默认的相等比较器由对象的T的IEquatable.Equals方法的实现(列表中的值的类型)定义。

http://msdn.microsoft.com/en-us/library/bhkz42b3(v=vs.110).aspx

您还需要覆盖Equals()。注意当重载Equals()时,几乎总是正确地覆盖GetHashCode()。

答案 2 :(得分:8)

覆盖EqualsGetHashCode,如:

class Element
{
    public int id;

    protected bool Equals(Element other)
    {
        return id == other.id;
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((Element) obj);
    }

    public override int GetHashCode()
    {
        return id; //or id.GetHashCode();
    }
 //..... rest of the class

请参阅:List<T>.Contains Method

  

此方法使用默认相等性确定相等性   比较器,由对象的实现定义   T的IEquatable<T>.Equals方法(列表中值的类型)。