Проверка наличия объекта в коллекции - PullRequest
4 голосов
/ 18 декабря 2010

Я изучаю программирование, и моя проблема в том, что у меня есть куча объектов, и я хочу добавить эти объекты в список, только если список еще не содержит этот объект. Во-вторых, если объект уже содержится, я хочу игнорировать этот объект и добавить следующий. Я думаю, что первая часть работает, мне нужна помощь с второй частью. Большое спасибо.

PartyGroup partyGroup = new PartyGroup(); 

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        if (!myPartyGroupList.Contains(partyGroup)) 
        { 
            partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
            partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
            partyGroup.PersonList = myPersonList; 

            myPartyGroupList.Add(partyGroup); 
        } 
        else 
        { 
            //?? 
        } 
    } 
} 

Ответы [ 6 ]

5 голосов
/ 18 декабря 2010

У тебя первая часть прекрасна.

Просто удалите предложение else, и ваша подпрограмма автоматически добавит следующий элемент в следующей итерации. Как это:

while (reader.Read()) 
{ 
    if (!myPartyGroupList.Contains(partyGroup)) 
    { 
        partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
        partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
        partyGroup.PersonList = myPersonList; 

        myPartyGroupList.Add(partyGroup); 

    } 
} 
4 голосов
/ 18 декабря 2010

Ваш код имеет некоторые проблемы.Во-первых, вы повторно используете один и тот же объект для каждой из ваших итераций.

Рассмотрим

List<Foo> foos = new List<Foo>();
Foo foo = new Foo();
foo.Bar = "Alpha";
foos.Add(foo);
foo.Bar = "Beta";
foos.Add(foo);

Вы заметите, что в вашем списке будет 2 элемента, но они будут ссылаться на один и тот же объект.Если вы перебираете список и проверяете Bar, каждый из них вернет "Beta".

Вы хотите создать новый Foo для каждого элемента.

List<Foo> foos = new List<Foo>();
Foo foo = new Foo();
foo.Bar = "Alpha";
foos.Add(foo);
Foo anotherFoo = new Foo();
anotherFoo.Bar = "Beta";
foos.Add(anotherFoo);

В терминах цикла это в основном означает создание объекта внутри цикла, а не снаружи.

while (someCondition)
{
    Foo foo = new Foo();
    // do your work, populate the object, etc.
    // then check contains 
    if (!myList.Contains(foo))
        myList.Add(foo);
}

Что касается проверки того, содержит ли коллекция объект, правильно ли вы переопределили Equals и GetHashCode?При работе с классами поведение по умолчанию заключается в простой проверке, равны ли ссылки на объекты.Если вы беспокоитесь о значениях , которые инкапсулируют объекты, то вам нужно предоставить логику для этого самостоятельно.В вашем классе вам нужно переопределить методы Equals и GetHashCode, чтобы реализовать желаемый метод определения равенства.

class Foo
{
    public string Bar { get; set; }

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

    public override bool Equals(object other)
    {
        Foo otherFoo = other as Foo;
        if (otherFoo == null)
            return false;
        else
            return this.Bar == otherFoo.Bar;
    }
}

Теперь, когда Contains пытается определить, находится ли объект уже всписок, он будет делать это на основе значений, содержащихся в объекте, а не ссылки на память.

3 голосов
/ 18 декабря 2010

При сравнении лучше использовать сравнение по идентификатору, который в вашем случае является PartyGroupId.Если вы используете содержит, то по умолчанию используется перегрузка Contains (), тогда для сравнения используется хэш-значение объекта в списке.

Поэтому вместо того, чтобы оставлять сравнение в .NET, вы можете создать собственную реализацию IEqualityComparer илииспользуйте условие Linq's Where следующим образом.


using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        int groupId = Convert.ToInt32(reader["party_group_id"]);

        if (partyGroupsList.Where(partyGroup => partyGroup.PartyGroupID == groupId).Any() == false)
        {
           PartyGroup newPartyGroup = new PartyGroup()
                                      {
                                          PartyGroupID = groupId,
                                          PartyGroupName = reader["party_group_name"].ToString(),
                                          PersonList = myPersonList
                                      };

           partyGroupsList.Add(newPartyGroup);                 
        } 

        // If object already exists in the list then do not add, continue 
        // to the next row.   
    } 
} 

Другое предложение состоит в том, чтобы переименовать членов класса PartyGroup как:


class PartyGroup
 {
   public int ID { get; set; }
   public string Name { get; set; }
   public IList PersonList { get; set; }
}
1 голос
/ 18 декабря 2010

Вы можете использовать Hashset<PartyGroup> для заполнения, а затем преобразовать его в список. Если у вас есть большое количество элементов, это будет значительно быстрее, чем проверка списка для каждого элемента.

Hashset<PartyGroup> pgHash = new Hashset<PartyGroup>();

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {
        PartyGroup pg = new PartyGroup();  
        partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
        partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
        partyGroup.PersonList = myPersonList; 
        // Add won't add an item if it already exists in the collection.
        pgHash.Add(partyGroup); 
    } 
}
// Now convert the result to a list.
myPartyGroupList = pgHash.ToList();

Если ваш PartyGroup класс не реализует IEquatable<PartyGroup>, вам придется предоставить средство сравнения на равенство. Должно работать следующее:

public class PartyGroupComparer:IEqualityComparer<PartyGroup>
{
    public bool Equals(PartyGroup g1, PartyGroup g2)
    {
        return g1.PartyGroupId.Equals(g2.PartyGroupId);
    }

    public int GetHashCode(PartyGroup g)
    {
        return g.PartyGroupId;
    }
}

И тогда ваша инициализация становится:

IEqualityComparer<PartyGroup> pgComparer = new PartyGroupComparer();
HashSet<PartyGroup> pgHash = new HashSet<PartyGroup>(pgComparer);

Альтернативой HashSet, как кто-то еще указал, является Dictionary. Это избавит вас от необходимости сравнивать равенство. Вам все равно придется преобразовать в список, когда вы закончите. Но это довольно просто:

Dictionary<int, PartyGroup> dict = new Dictionary<int, PartyGroup>();
// populate dictionary as suggested in other answer

// now convert values to a list.
myPartyGroupList = dict.Values.ToList();
0 голосов
/ 18 декабря 2010

Сначала вы проверяете, находится ли экземпляр объекта в коллекции, но вы создаете только один экземпляр один раз (вне цикла while). Поэтому, если вы проверите, если !myPartyGroupList.Contains(partyGroup), он вернет false в первый раз, поэтому вы добавите объект obj в коллекцию, а затем он будет возвращать false каждый раз.

Я бы использовал словарь, используя свойство Id в качестве ключа словаря.

как это:

Dictionary <int,PartyGroup> myPartyGroupList = new Dictionary <int,PartyGroup>();

using (AseDataReader reader = command.ExecuteReader()) 
{ 
    while (reader.Read()) 
    {  
        int id=Convert.ToInt32(reader["party_group_id"]); 
        if (!myPartyGroupList.ContainsKey( id )) 
        { 
            PartyGroup partyGroup = new PartyGroup(); 

            partyGroup.PartyGroupID = id; 
            partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
            partyGroup.PersonList = myPersonList; 

            myPartyGroupList.Add(id, partyGroup);  // key, value? check param order here
        } 
    } 
} 
0 голосов
/ 18 декабря 2010

Попробуйте это

while (reader.Read()) 
{ 
    partyGroup.PartyGroupID = Convert.ToInt32(reader["party_group_id"]); 
    partyGroup.PartyGroupName = reader["party_group_name"].ToString(); 
    partyGroup.PersonList = myPersonList; 

    if (!myPartyGroupList.Contains(partyGroup)) 
    { 
        myPartyGroupList.Add(partyGroup); 
    } 
} 
...