Извлечь элемент в списке, если Contains возвращает true - PullRequest
1 голос
/ 23 апреля 2010

У меня есть два списка A и B, в начале моей программы они оба заполняются информацией из базы данных (Список A = Список B).Моя программа запускается, список A используется и изменяется, список B остается один.Через некоторое время я перезагружаю список B новой информацией из базы данных, а затем проверяю ее по списку A.

foreach (CPlayer player in ListA)
      if (ListB.Contains(player))
            -----

Во-первых, объект player создается из класса, его основным идентификатором является player.Название.Если имя такое же, но другие переменные разные, будет ли .Contains по-прежнему возвращать true?

Class CPlayer(
      public CPlayer (string name)
              _Name = name

В ---- Мне нужно использовать элемент из ListB, который вызывает .Containsчтобы вернуть истину, как мне это сделать?

Ответы [ 5 ]

5 голосов
/ 23 апреля 2010

Поведение по умолчанию List.Contains заключается в том, что используется компаратор равенства по умолчанию. Если ваши элементы являются ссылочными типами, это означает, что он будет использовать сравнение идентификаторов, если ваш класс не предоставит другую реализацию через Equals.

Если вы используете .NET 3.5, вы можете изменить вторую строку на эту, которая будет делать то, что вы хотите:

if (ListB.Any(x => x.Name == player.Name))

Для .NET 2.0 вы можете реализовать Equals и GetHashCode для вашего класса, но это может привести к нежелательному поведению в других ситуациях, когда вы не хотите, чтобы два объекта игрока сравнивались одинаково, если они имеют одинаковое имя, но различаются в других областях.

Альтернативный способ - адаптировать ответ Джона Скита для .NET 2.0. Создайте Dictionary<string, object> и заполните его именами всех игроков в списке B. Затем, чтобы проверить, находится ли игрок с определенным именем в списке B, вы можете использовать dict.ContainsKey(name).

2 голосов
/ 23 апреля 2010

Альтернативой предложению Марка является создание набора имен и использование этого:

HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name));
foreach (CPlayer player in ListA)
{
    if (namesB.Contains(player.Name))
    {
        ...
    }
}
0 голосов
/ 23 апреля 2010

Если вы хотите, чтобы метод .Contains совпадал только с CPlayer.Name, то в классе CPlayer реализуйте эти методы:

public override bool Equals(object obj)
{
    if (!(obj is CPlayer)
        return false;
    return Name == (obj as CPlayer).Name;
}
public override int GetHashCode()
{
    return Name.GetHashCode();
}

Если вы хотите, чтобы сравнение Name не чувствительно к регистру, замените его, вместо этого используйте метод Equals:

public override bool Equals(object obj)
{
    if (!(obj is CPlayer)
        return false;
    return Name.Equals((obj as CPlayer).Name, StringComparison.OrdinalIgnoreCase);
}

Если вы сделаете это, ваш вызов .Contains будет работать так, как вы этого хотите. Во-вторых, если вы хотите выбрать этот элемент в списке, сделайте следующее:

var playerB = ListB[ListB.IndexOf(player)];

Используются те же методы .Equals и .GetHashCode.

UPD: Это, вероятно, субъективное утверждение, но вы также можете уменьшить его производительность, если ваш метод .Equals сравнил хэши Int перед выполнением сравнения строк.

Глядя на источники .NET (Reflector FTW), я вижу, что, по-видимому, только класс HastTable использует GetHashCode для повышения своей производительности вместо использования .Equals для сравнения объектов каждый раз. В случае небольшого класса, подобного этому, средство сравнения равенства является простым, сравнение одной строки. Если бы вы сравнивали все свойства, сравнение двух целых чисел было бы намного быстрее (особенно если бы они были кэшированы :))

List.Contains и List.IndexOf не используют хеш-код, а используют метод .Equals, поэтому я предложил проверить хеш-код внутри. Это, вероятно, не будет ничего заметного, но когда вы жаждете получить каждую мс выполнения (не всегда хорошая вещь, ошибка, эй!: P), это может кому-то помочь. просто говорю ...:)

0 голосов
/ 23 апреля 2010

Похоже, это то, что вам нужно сделать:

Для каждого игрока в списке A найдите каждого игрока в списке B с одинаковыми именами и поместите обоих игроков в одну область. Вот подход, который объединяет два списка в запросе:

var playerPairs =
    from playerA in ListA
    join playerB in ListB on playerA.Name equals playerB.Name
    select new { playerA, playerB };

foreach(var playerPair in playerPairs)
{
    Console.Write(playerPair.playerA.Name);
    Console.Write(" -> ");
    Console.WriteLine(playerPair.playerB.Name);
}
0 голосов
/ 23 апреля 2010

Предполагая, что вы используете класс System.Collections.Generic.List, если класс CPlayer не реализует IEquatable<T>, он будет использовать функции Equals и GetHashCode класса CPlayer, чтобы проверить, является ли Listимеет члена, равного аргументу Contains.Предполагая, что реализация вам подходит, вы могли бы что-то вроде

CPlayer listBItem = ListB.First(p => p == player);

, чтобы получить экземпляр из ListB

...