Сбой List.Contains при сравнении объектов

У меня есть класс "Class1", который имеет строковую переменную "sText" в.NET 2.0. Я создал список объектов этого класса "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", даже если он уже присутствует в списке.

Обратите внимание: я не взял простой список строк или строк, потому что я хочу объяснить мою большую проблему со списком, классами и объектами простым способом.

Пожалуйста помоги.

4 ответа

Решение

Использование Any() метод:

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

Этот код:

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

Может работать только если вы предоставите Equals() а также GetHashCode() методы для вашего Class1 включить сравнение между двумя объектами этого класса. Для достижения этого ваш класс должен реализовать 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();
    }
}

Contains будет работать корректно только в том случае, если вы реализуете IEquatable в вашем случае.

Вместо этого вы можете использовать следующий код:

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();
}

Раскомментируйте закомментированные строки, и вы увидите разницу.

Проблема, с которой вы здесь сталкиваетесь, заключается между объектом и экземпляром. Когда вы проверяете следующее, вы в основном спрашиваете "есть ли в коллекции?"

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

Ну, это, очевидно, не может быть, так как вы только что создали это. Вам нужно проверить содержимое этого объекта. Самый простой способ сделать это - перебрать коллекцию и проверить содержимое каждого объекта.

Это очень легко сделать с помощью LINQ (хотя я не уверен, что он доступен для.NET 2.0):

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

Другое решение было бы переписать GetHashCode() а также Equals методы вашего Class1, чтобы рассматривать два объекта как "равные", если их содержимое одинаковое. и, таким образом, позволяют провести тщательное сравнение.

Это не удается, потому что список звонков 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;
}
Другие вопросы по тегам