I have two lists A and B, at the beginning of my program, they are both filled with information from a database (List A = List B). My program runs, List A is used and modifi开发者_开发问答ed, List B is left alone. After a while I reload List B with new information from the database, and then do a check with that against List A.
foreach (CPlayer player in ListA)
if (ListB.Contains(player))
-----
Firstly, the object player is created from a class, its main identifier is player.Name. If the Name is the same, but the other variables are different, would the .Contains still return true?
Class CPlayer(
public CPlayer (string name)
_Name = name
At the ---- I need to use the item from ListB that causes the .Contains to return true, how do I do that?
The default behaviour of List.Contains
is that it uses the default equality comparer. If your items are reference types this means that it will use an identity comparison unless your class provides another implementation via Equals
.
If you are using .NET 3.5 then you can change your second line to this which will do what you want:
if (ListB.Any(x => x.Name == player.Name))
For .NET 2.0 you could implement Equals
and GetHashCode
for your class, but this might give undesirable behaviour in other situations where you don't want two player objects to compare equal if they have the same name but differ in other fields.
An alternative way is to adapt Jon Skeet's answer for .NET 2.0. Create a Dictionary<string, object>
and fill it with the names of all players in listB. Then to test if a player with a certain name is in listB you can use dict.ContainsKey(name)
.
An alternative to Mark's suggestion is to build a set of names and use that:
HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name));
foreach (CPlayer player in ListA)
{
if (namesB.Contains(player.Name))
{
...
}
}
Assuming you are using the System.Collections.Generic.List
class, if the CPlayer
class does not implement IEquatable<T>
it will use the Equals
and GetHashCode
functions of the CPlayer
class to check if the List
has a member that equals the argument of Contains
. Assuming that implementation is OK for you, you could something like
CPlayer listBItem = ListB.First(p => p == player);
to get the instance from ListB
It sounds like this is what you need to accomplish:
For each player in list A, find each player in list B with the same name and bring both players into the same scope.
Here is an approach which joins the two lists in a query:
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);
}
If you want the .Contains method to match only on CPlayer.Name, then in the CPlayer class implement these methods:
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();
}
If you want the Name
comparison to be Case Insensitive, replace use this Equals method instead:
public override bool Equals(object obj)
{
if (!(obj is CPlayer)
return false;
return Name.Equals((obj as CPlayer).Name, StringComparison.OrdinalIgnoreCase);
}
If you do this, your .Contains call will work just as you want it. Secondly, if you want to select this item in the list, do this:
var playerB = ListB[ListB.IndexOf(player)];
It uses the same .Equals and .GetHashCode methods.
UPD: This is probably a subjective statement, but you could also squeeze some performance out of it, if your .Equals method compared the Int hashes before doing the string comparison..
Looking at the .NET sources (Reflector FTW) I can see that seemingly only the HastTable class uses GetHashCode to improve it's performance, instead of using .Equals to compare objects every single time. In the case of a small class like this, the equality comparer is simple, a single string comparison.. If you were comparing all properties though, then comparing two integers would be much faster (esp if they were cached :) )
The List.Contains and List.IndexOf don't use the hash code, and use the .Equals method, hence I proposed checking the hash code inside. It probably won't be anything noticeable, but when you're itching to get every single ms of execution (not always a good thing, bug hey! :P ) this might help someone. just saying... :)
精彩评论