【问题标题】:List.Except is not workingList.Except 不起作用
【发布时间】:2012-10-20 12:00:49
【问题描述】:

我尝试减去 2 个列表,如下面的代码,assignUsers 有 3 条记录,assignedUsers 有 2 行。在Except 方法之后我仍然得到3 行,虽然我应该得到1 条记录,因为assignedUsers 中的2 行类似于assignUsers

 var users = accountApp.GetUsersByAccountId(context.GetUserData().AccountId);
 List<AssignUserViewModel> assignUsers = Mapper.Map<List<AssignUserViewModel>>(users).ToList();
 var mailUsers = mailApp.GetMailAssignedByMailId(id).Select(m => new { m.UserId, m.User.Name }).ToList();
 List<AssignUserViewModel> assignedUsers = mailUsers.Select(Mapper.DynamicMap<AssignUserViewModel>).ToList();
 assignUsers = assignUsers.Except(assignedUsers).ToList();

【问题讨论】:

  • 您的映射器可能正在丢失引用,并且该类型可能没有定义其他比较器。
  • 使用 IComparerer 编写比较函数。
  • @leppie 您应该将其发布为答案:)
  • 如何正确地将匿名类型映射到其他类型?如果是这样的话

标签: c# linq list


【解决方案1】:

为了使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&lt;&gt; 并将其传递给 Except 方法,例如:

class AssignUserViewModelEqualityComparer : IEqualityComparer<AssignUserViewModel>
{
    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();

【讨论】:

  • 已编辑。稍微更改了相等比较器 Equals 以考虑空值。
【解决方案2】:

为什么会这样? 当您使用Set Operations(Distinct、Except、Intersect、Union)时,Linq 需要比较序列元素是否相等。默认情况下,Linq 使用 Object.EqualsObject.GetHashCode 方法来比较元素。如果这些方法没有在您的类型中被覆盖,则使用基类的实现,它通过引用相等来比较对象。默认实现保证相同引用的两个对象具有相同的哈希码(因此被认为相等)。这是你的情况。 Mapper 类创建 AssignUserViewModel 对象的新实例,这些对象具有不同的引用,并且不能被视为相等(即使所有字段值都相同)。

那么,我们可以用这个做什么呢?

  • 覆盖类中的 EqualsGetHashCode 方法。您将如何对待对象平等 - 所有字段,或只是身份,这取决于您。 Linq 将使用您的方法来比较元素。

  • 提供您自己的比较器(这通常是您无法修改对象并覆盖 EqualsGetHashCode 的情况。是的,所有 Linq 集操作都有两个重载 - 一个使用默认比较器,另一个,接受你的IEqualityComparer&lt;T&gt;

  • 使用匿名类型。所有匿名类型都已经生成了方法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<AssignUserViewModel> result = 
             assignUsers.Select(Mapper.DynamicMap<AssignUserViewModel>).ToList();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-06-05
    • 2016-04-22
    • 1970-01-01
    • 2016-03-13
    • 1970-01-01
    • 1970-01-01
    • 2016-09-12
    • 1970-01-01
    相关资源
    最近更新 更多