C# Linq/可枚举任何Vs包含
我已经解决了我遇到的一个问题,但尽管我已经发现了某些东西是如何工作的(或不工作的),但我不清楚为什么 因为我是那种喜欢知道“为什么”的人,我希望有人能解释: 我有项目列表和相关评论,我想区分管理员评论和用户评论,所以我尝试了以下代码:C# Linq/可枚举任何Vs包含,c#,linq,enumerable,C#,Linq,Enumerable,我已经解决了我遇到的一个问题,但尽管我已经发现了某些东西是如何工作的(或不工作的),但我不清楚为什么 因为我是那种喜欢知道“为什么”的人,我希望有人能解释: 我有项目列表和相关评论,我想区分管理员评论和用户评论,所以我尝试了以下代码: User commentUser = userRepository.GetUserById(comment.userId); Role commentUserRole = context.Roles.Single(x=>x.Name == "admin");
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
}
逐步检查代码表明,尽管它具有正确的角色对象,但它无法识别commentUser.Roles中的角色
最终起作用的代码是:
if(commentUser.Roles.Any(x=>x.Name == "admin"))
{
//do stuff
}
我很高兴这样做,因为它的代码更少,而且在我看来更干净,但我不明白contains是如何不起作用的
希望有人能帮我澄清。这可能是因为您没有覆盖
角色类上的相等比较(等于,GetHashCode
,操作符==
)。因此,它在做参考比较,这真的不是最好的主意,好像它们不是同一个对象,它使它认为这是不同的。您需要重写这些相等运算符以提供值相等。这将是角色的相等比较
commentUserRole
中的对象与您要查找的对象不同
当您从上下文对象中选择并用新角色集合填充角色属性时,上下文对象将创建一个新对象。如果您的上下文没有跟踪对象,以便在请求第二个副本时返回相同的对象,那么它将是不同的对象,即使所有属性可能相同。因此,控制的失败
您的Any
子句正在显式检查Name属性,这就是它工作的原因
尝试使角色实现IEquatable
使用Contains
-方法时,检查用户对象的数组Roles
是否包含您事先从数据库检索到的对象。虽然数组包含角色“admin”的对象,但它不包含您以前获取的确切对象
当使用Any
-方法时,您将检查是否有任何名为“admin”的角色,并且该角色可以提供预期的结果
要使用Contains
-方法获得相同的结果,请在role类上实现IEquatable
-接口,并比较名称以检查两个实例是否实际具有相同的值。您必须重写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;
}
}
另一个选项是为以下内容的重载实现自定义IEqualityComparer
:
我不明白-User.Roles不是角色对象列表吗?因为他没有在角色对象之间使用=
,所以如果他重载操作符==
,这并不重要,但是,覆盖Equals
和GetHashCode
@richardtries是正确的,这取决于对象上下文如何处理对同一对象的第二个请求。看起来行为是创建一个新对象,而不是返回已请求的对象。这意味着即使所有属性值都是相同的,它们实际上是不同的对象,没有相等覆盖,值返回false。@JeppeStigNielsen只是为了一致性:我知道在这个例子中他不需要它。谢谢你的帮助-我已经对'it'sNotALie'的注释进行了评论-但是我想如果Role
是一个不是密封的
,有些人更喜欢使用return GetType()==r2.GetType()&&RoleID==r2.RoleID代码>。否则(即使是细心的程序员)也很难从角色
中派生出他们的等价物
。@JeppeStigNielsen:我已经添加了另一个选项,他可以使用IEqualityComparer
。这样他就不需要修改角色本身了。我喜欢这样。我的评论只与您的第一个“选项”相关,即修改角色
类型本身,既然您没有从答案中删除该选项,我仍然想对此发表评论。我想问题是,这是否比使用.Any检查名称更好?@richardterris:不一定,这都是关于可读性的:如果您经常需要这个类,并且需要经常相互比较角色(例如,在Enumerable.Contains
)中,最好重写Equals
和GetHashCode
。如果你能修改这个类,这是最好的做法。如果不能,您仍然可以使用自定义的IEqualityComparer
,如上所示。然后,您甚至可以在其他linq方法中使用比较器,如GroupBy
、Intersect
、Distinct
、Join
等。。如果您无法覆盖Equals
+GetHashCode
,则所有这些方法都会为IEqualityComparer
提供重载版本。
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();
}
}
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;
}
}
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))
{
// ...
}