Linq/Enumerable Any Vs Contains

本文关键字:Vs Contains Any Enumerable Linq | 更新日期: 2023-09-27 18:26:25

我解决了一个问题,但尽管我发现了一些东西是如何工作的(或不工作),但我不清楚为什么。

由于我是那种喜欢知道"为什么"的人,我希望有人能解释:

我有项目和相关评论的列表,我想区分管理员评论和用户评论,所以我尝试了以下代码:

User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole)
 {
   //do stuff
 }
else
{
 // do other stuff
}

仔细查看代码可以发现,尽管它有正确的Role对象,但它无法识别commentUser.Roles 中的角色

最终起作用的代码是:

if(commentUser.Roles.Any(x=>x.Name == "admin"))
{
  //do stuff
}

我对此感到高兴,因为它的代码更少,在我看来更干净,但我不明白contains是如何不起作用的。

希望有人能帮我澄清。

Linq/Enumerable Any Vs Contains

这可能是因为您没有覆盖Role类上的相等比较(EqualsGetHashCodeoperator==)。因此,它在做参考比较,这真的不是最好的主意,就好像它们不是同一个对象,这会让它认为它是不同的。您需要重写这些相等运算符以提供值相等。

如果要使用Contains,则必须覆盖Equals(然后始终覆盖GetHashCode)。否则Equals将仅比较引用。

例如:

public class Role
{
    public string RoleName{ get; set; }
    public int RoleID{ get; set; }
    // ...
    public override bool Equals(object obj)
    {
        Role r2 = obj as Role;
        if (r2 == null) return false;
        return RoleID == r2.RoleID;
    }
    public override int GetHashCode()
    {
        return RoleID;
    }
    public override string ToString()
    {
        return RoleName;
    } 
}

另一种选择是为Enumerable.Contains:的过载实现自定义IEqualityComparer<Role>

public class RoleComparer : IEqualityComparer<Role>
{
    public bool Equals(Role x, Role y)
    {
        return x.RoleID.Equals(y.RoleID);
    }
    public int GetHashCode(Role obj)
    {
        return obj.RoleID;
    }
}

以这种方式使用:

var comparer = new RoleComparer();
User commentUser = userRepository.GetUserById(comment.userId);
Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
if(commentUser.Roles.Contains(commentUserRole, comparer))
{
    // ...
}

使用Contains-方法时,检查用户对象的数组Roles是否包含预先从数据库中检索到的对象。尽管数组包含角色"admin"的对象,但它并不包含您之前提取的确切对象。

当使用Any方法时,您会检查是否有任何名称为"admin"的角色,并提供预期的结果。

为了使用Contains-方法获得相同的结果,在角色类上实现IEquatable<Role>-接口,并比较名称以检查两个实例是否实际具有相同的值。

这将是您对角色的平等比较。

commentUserRole中的对象与您要查找的commentUser.Roles中的对象不同。

当您从上下文对象中选择并用新角色的集合填充Roles属性时,上下文对象将创建一个新对象。如果您的上下文在请求第二个副本时没有跟踪对象以返回相同的对象,那么即使所有属性都可能相同,它也将是不同的对象。因此Contains 失败

您的Any子句显式检查Name属性,这就是它在中工作的原因

尝试让角色实现IEquatable<Role>

public class Role : IEquatable<Role> {
  public bool Equals(Role compare) {
    return compare != null && this.Name == compare.Name;
  }
}

虽然MSDN显示您只需要List<T>,但实际上可能需要覆盖EqualsGetHashCode才能实现

在这种情况下:

public class Role : IEquatable<Role> {
  public bool Equals(Role compare) {
    return compare != null && this.Name == compare.Name;
  }
  public override bool Equals(object compare) {
     return this.Equals(compare as Role); // this will call the above equals method
  }
  public override int GetHashCode() {
     return this.Name == null ? 0 : this.Name.GetHashCode();
  }
}