为什么List.Contains没有像我期望的那样工作?

use*_*077 26 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");
    }
}
Run Code Online (Sandbox Code Playgroud)

该Contains方法不使用==运算符.问题是什么?

Jon*_*eet 57

Contains方法不使用==运算符

不 - 它使用Equals,你没有覆盖...所以你得到的默认行为Equals,即检查参考标识.你应该覆盖Equals(object)并GetHashCode保持彼此一致 - 为了理智,与你的==过载一致.

我还建议实施IEquatable<Element>,List<Element>优先使用Equals(object),作为EqualityComparer<T>.Default适当的选择.

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

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

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!
Run Code Online (Sandbox Code Playgroud)

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

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

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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


Eri*_* J. 12

Contains方法不使用==运算符.问题是什么?

那是正确的.

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

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

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


use*_*965 8

覆盖Equals和GetHashCode喜欢:

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
Run Code Online (Sandbox Code Playgroud)

看到: List<T>.Contains Method

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

  • 谢谢.你的代码简化了我的工作.如果有可能我也接受你的回答. (3认同)