1

我有两个列表 A 和 B,在我的程序开始时,它们都填充了来自数据库的信息(列表 A = 列表 B)。我的程序运行,列表 A 被使用和修改,列表 B 被单独留下。过了一会儿,我用数据库中的新信息重新加载列表 B,然后对照列表 A 进行检查。

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

首先,对象 player 是从一个类创建的,它的主要标识符是 player.Name。如果 Name 相同,但其他变量不同,.Contains 是否仍会返回 true?

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

在 ---- 我需要使用 ListB 中导致 .Contains 返回 true 的项目,我该怎么做?

4

5 回答 5

5

的默认行为List.Contains是它使用默认的相等比较器。如果您的项目是引用类型,这意味着它将使用身份比较,除非您的类通过Equals.

如果您使用的是 .NET 3.5,那么您可以将第二行更改为此将执行您想要的操作:

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

对于 .NET 2.0,您可以为您的类实现EqualsGetHashCode,但这可能会在您不希望两个播放器对象比较相等(如果它们具有相同名称但在其他字段中不同)的其他情况下产生不良行为。

另一种方法是改编Jon Skeet对 .NET 2.0 的回答。创建 aDictionary<string, object>并用 listB 中所有玩家的名字填充它。然后测试具有特定名称的玩家是否在 listB 中,您可以使用dict.ContainsKey(name).

于 2010-04-23T06:06:53.327 回答
2

Mark 建议的替代方法是建立一组名称并使用它:

HashSet<string> namesB = new HashSet<string>(ListB.Select(x => x.Name));
foreach (CPlayer player in ListA)
{
    if (namesB.Contains(player.Name))
    {
        ...
    }
}
于 2010-04-23T06:12:26.170 回答
0

假设您正在使用System.Collections.Generic.List该类,如果CPlayer该类未实现IEquatable<T>,它将使用该类的EqualsandGetHashCode函数CPlayer来检查 是否List有一个等于 的参数的成员Contains。假设实施对您来说可以,您可以像

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

从中获取实例ListB

于 2010-04-23T06:23:12.460 回答
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);
}
于 2010-04-23T06:34:38.643 回答
0

如果您希望 .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 方法,因此我建议检查里面的哈希码。它可能不会有什么明显的,但是当你渴望得到每一个毫秒的执行时(并不总是一件好事,bug 嘿!:P)这可能会对某人有所帮助。只是说... :)

于 2010-04-23T08:39:19.663 回答