List.Except不起作用
我尝试减去2个列表,如下面的代码, assignUsers
有3条记录, assignUsers
有2行。 在Except
方法之后我仍然得到3行,虽然我应该获得1条记录,因为assignedUsers
2行类似于assignUsers
var users = accountApp.GetUsersByAccountId(context.GetUserData().AccountId); List assignUsers = Mapper.Map<List>(users).ToList(); var mailUsers = mailApp.GetMailAssignedByMailId(id).Select(m => new { m.UserId, m.User.Name }).ToList(); List assignedUsers = mailUsers.Select(Mapper.DynamicMap).ToList(); assignUsers = assignUsers.Except(assignedUsers).ToList();
为了使Except
方法按预期工作, AssignUserViewModel
类必须正确覆盖GetHashCode
和Equals
方法。
例如,如果AssignUserViewModel
对象由其Id
唯一定义,则应以这种方式定义类:
class AssignUserViewModel { // other methods... public override int GetHashCode() { return this.Id.GetHashCode(); } public override bool Equals(object obj) { if (!(obj is AssignUserViewModel)) throw new ArgumentException("obj is not an AssignUserViewModel"); var usr = obj as AssignUserViewModel; if (usr == null) return false; return this.Id.Equals(usr.Id); } }
否则,如果您不能/不想更改类实现,则可以实现IEqualityComparer<>
并将其传递给Except
方法,例如:
class AssignUserViewModelEqualityComparer : IEqualityComparer { public bool Equals(AssignUserViewModel x, AssignUserViewModel y) { if (object.ReferenceEquals(x, y)) return true; if(x == null || y == null) return false; return x.Id.Equals(y.Id); } public int GetHashCode(AssignUserViewModel obj) { return obj.Id.GetHashCode(); } }
然后你的最后一行将成为:
assignUsers = assignUsers.Except(assignedUsers, new AssignUserViewModelEqualityComparer()).ToList();
为什么会这样? 当您使用Set Operations (Distinct,Except,Intersect,Union)时,Linq需要比较sequence(s)元素是否相等。 默认情况下,Linq使用Object.Equals和Object.GetHashCode方法来比较元素。 如果未在类型中覆盖这些方法,则使用基类的实现,该实现通过引用相等性比较对象。 默认实现保证两个具有相同引用的对象具有相同的哈希码(因此被认为是相等的)。 这是你的情况。 Mapper
类创建AssignUserViewModel
对象的新实例, AssignUserViewModel
对象具有不同的引用,并且不能被视为相等(即使所有字段值都相同)。
那么,我们能做些什么呢?
-
覆盖类中的
Equals
和GetHashCode
方法。 由您决定如何对待对象 – 所有领域或仅仅是身份。 Linq将使用您的方法来比较元素。 -
提供你自己的比较器(这通常是你不能修改你的对象并覆盖
Equals
和GetHashCode
。是的,所有Linq Set Operations都有两个重载 – 一个使用默认比较器,另一个接受你的IEqualityComparer
。 -
使用匿名类型。 所有匿名类型都已生成方法
Equals
和GetHashCode
,它们使用所有属性的比较来确定对象是否相等。 在这种情况下,您既不需要修改类型也不需要创建比较器。
因此,您已经有前两种方法的样本,这是最后一种:
var assignUsers = accountApp.GetUsersByAccountId(context.GetUserData().AccountId) .Select(u => new { u.UserId, u.Name }); var assignedUsers = mailApp.GetMailAssignedByMailId(id) .Select(m => new { m.UserId, m.User.Name }); var assignUsers = assignUser.Except(assignedUsers); // do not map until here List result = assignUsers.Select(Mapper.DynamicMap ).ToList();