2010-04-23 5 views
1

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

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

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

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

На ---- мне нужно использовать элемент из LISTB, что приводит к тому, .Contains вернуться так, как мне это сделать?

ответ

5

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

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

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

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

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

+0

.NET 2.0 к сожалению – Matt

+0

@Matt: теперь добавлено возможное решение для .NET 2.0. –

2

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

HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name)); 
foreach (CPlayer player in ListA) 
{ 
    if (namesB.Contains(player.Name)) 
    { 
     ... 
    } 
} 
0

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

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

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

0

Это звучит, как это то, что вам нужно выполнить:

Для каждого игрока в списке 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

Если вы хотите метод .Contains соответствовать только CPlayer.Имя, то в классе 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 сравнения быть Case Нечувствительностью, заменить использование этого Равны методы вместо:

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

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

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

Он использует те же .equals и методы .GetHashCode.

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

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

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

+0

Можете ли вы предоставить некоторые доказательства или обоснования для «Вы могли бы также выжать из него какую-то производительность, если ваш метод .Equals сравнил хэши Int, прежде чем выполнять сравнение строк ...»? –

+0

добавил объяснение в сообщении. Это частично субъективно .. но только частично) –