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类必须正确覆盖GetHashCodeEquals方法。

例如,如果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对象具有不同的引用,并且不能被视为相等(即使所有字段值都相同)。

那么,我们能做些什么呢?

  • 覆盖类中的EqualsGetHashCode方法。 由您决定如何对待对象 – 所有领域或仅仅是身份。 Linq将使用您的方法来比较元素。

  • 提供你自己的比较器(这通常是你不能修改你的对象并覆盖EqualsGetHashCode 。是的,所有Linq Set Operations都有两个重载 – 一个使用默认比较器,另一个接受你的IEqualityComparer

  • 使用匿名类型。 所有匿名类型都已生成方法EqualsGetHashCode ,它们使用所有属性的比较来确定对象是否相等。 在这种情况下,您既不需要修改类型也不需要创建比较器。

因此,您已经有前两种方法的样本,这是最后一种:

 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();