【问题标题】:List.Contains fails on object comparisonList.Contains 对象比较失败
【发布时间】:2014-02-07 10:14:09
【问题描述】:

我有一个类“Class1”,它在 .NET 2.0 中有一个字符串变量“sText”。我已经创建了该类“lstClass1”的对象列表。在设置了它的字符串变量后,它存储了该类的许多对象。

完整代码为:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        if (!lstClass1.Contains(new Class1("text1")))
            lstClass1.Add(new Class1("text1"));
    }

    public List<Class1> lstClass1 = new List<Class1>();
}

public class Class1
{
    public Class1(string sText)
    {
        this.sText = sText;
    }

    public string sText = "";
}

现在,问题是我只想添加具有唯一文本值的字符串的类对象。例如,如果 lstClass1 已经有一个带有字符串变量值“text1”的对象,那么它不应该允许添加一个也有“text1”的对象。所以,我写了代码:

if (!lstClass1.Contains(new Class1("text1")))
    lstClass1.Add(new Class1("text1"));

但它总是允许添加文本“text1”,即使列表中已经有一个带有“text1”字符串的对象。我的假设是,在第一个按钮单击事件“button1_Click”时,将添加对象,因为列表为空,但在下一个按钮单击 List.Contains 函数将检查列表中是否已经存在带有字符串变量“text1”的对象和如果找到,则不会添加。但它总是允许添加带有文本“text1”的对象,即使它已经存在于列表中。

请注意:我没有采用简单的字符串列表或字符串列表,因为我想以简单的方式解释我的列表、类和对象的大问题。

【问题讨论】:

  • 您是否提供了 Class1 的默认 Equals 和 GetHashCode 方法的实现?
  • 不确定为什么不将 new Class1("text1") 存储到新的 Class1 类型变量中,顺便说一句。没有必要构建它两次。只需构造一次,在变量上检查,然后从变量中存储。

标签: c# .net list class object


【解决方案1】:

使用Any()方法:

if (!lstClass1.Any(x => x.sText == "text1"))
    lstClass1.Add(new Class1("text1"));

这段代码:

if (!lstClass1.Contains(new Class1("text1")))
    lstClass1.Add(new Class1("text1"));

只有在您为您的Class1 提供Equals()GetHashCode() 方法以允许在此类的两个对象之间进行比较时才能工作。要实现这一点,您的类应该实现IEquatable 接口。 所以你的 Class1 可能看起来像这样:

public class Class1 : IEquatable<Class1>
{
    public Class1(string sText)
    {
        this.sText = sText;
    }

    public string sText = "";

    public bool Equals(Class1 other) 
    {
      if (other == null) 
         return false;

      if (this.sText == other.sText)
         return true;
      else
         return false;
    }

    public override int GetHashCode()
    {
      return this.sText.GetHashCode();
    }
}

【讨论】:

    【解决方案2】:

    只有在您实现 IEquatable 的情况下,Contains 才能正常工作。

    您可以改用以下代码:

    public class Class1 //: IEquatable<Class1>
    {
        public string sText = "";
        public Class1(string sText)
        {
            this.sText = sText;
        }
    
        //public bool Equals(Class1 other)
        //{
        //    return this.sText == other.sText;
        //}
    }
    static void Main(string[] args)
    {
        List<Class1> lstClass1 = new List<Class1>() { new Class1("text1") };
        if (!lstClass1.Contains(new Class1("text1")))
            lstClass1.Add(new Class1("text1"));
        Console.WriteLine(lstClass1.Count);
        Console.ReadKey();
    }
    

    取消注释已注释的行,您会看到不同之处。

    【讨论】:

      【解决方案3】:

      您在这里面临的问题是对象和实例之间的问题。当您检查以下内容时,您基本上是在问“在集合中吗?”

      !lstClass1.Contains(new Class1("text1"))
      

      显然不可能,因为您刚刚创建了它。您需要检查的是该对象的内容。最简单的方法是遍历集合并检查每个对象的内容。

      使用 LINQ 很容易做到这一点(虽然我不确定它是否适用于 .NET 2.0):

      !lstClass1.Any(i => i.Text == "text1")
      

      另一种解决方案是覆盖 Class1 的 GetHashCode()Equals 方法,以将两个对象视为“相等”(如果它们的内容相同)。从而进行彻底的比较。

      【讨论】:

        【解决方案4】:

        这会失败,因为列表在您的实例上调用 Equals,并且默认实现会检查引用是否相等。尽管您的对象具有 name 属性值,但它是一个差异实例,因此引用将不相等。

        Class1 中重写Equals 方法来比较底层属性

        public override bool Equals(object other)
        {
          Class1 rhs=other as Class1;
          if(rhs==null) return false;
        
          return this.sText == rhs.sText;
        }
        

        【讨论】:

          猜你喜欢
          • 2019-03-13
          • 2015-11-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-07
          • 2014-05-03
          • 1970-01-01
          • 2016-05-27
          相关资源
          最近更新 更多