【问题标题】:IEnumerable.Except() between different classes with a common field具有公共字段的不同类之间的 IEnumerable.Except()
【发布时间】:2010-11-01 15:59:39
【问题描述】:

是否可以将Except() 用于两个具有两个不同类但有一个公共字段的列表?我有List<User1>List<User2> 收藏。除了 Id 列之外,它们具有不同的属性,我想使用此 Id 列查找它们之间的不同记录。我正在尝试使用List<>.Except(),但出现此错误:

无法从用法中推断方法“System.Linq.Enumerable.Except(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)”的类型参数。尝试明确指定类型参数。

这是我正在尝试的:

List<User1> list1 = List1();
List<User2> list2 = List2();
var listdiff = list1.Except(list2.Select(row => row.Id));

我做错了什么?

【问题讨论】:

  • 请阅读this著名博客了解Enumerable.Except()方法的内部。

标签: c# linq .net-3.5 ienumerable


【解决方案1】:

List1 包含 User1 的实例,List2 包含 User2 的实例。

list1.Except(list2.Select(row =&gt; row.Id)) 应该生成什么类型​​的实例? 换句话说,如果类型推断不可用,你会用什么替换var

如果 User1 和 User2 继承自同一个祖先(带有 ID),请改用 List&lt;User&gt;

否则:

var list2Lookup = list2.ToLookup(user => user.Id);
var listdiff = list1.Where(user => (!list2Lookup.Contains(user.Id))

【讨论】:

  • +1 如果您在 list2 而不是 list1 上进行查找。或不查找 var listdiff = list1.Where(user =&gt; !(list2.Any(user2 =&gt; user2.Id == user.Id));
  • 是的,几分钟前就注意到了这个错误
  • 请注意,这与Except 的语义并不完全相同。 Except 返回两个序列的“集差”;也就是说,仅返回第一个序列中的唯一项(并且显然,仅在它们不在第二个序列中时才返回)。 msdn.microsoft.com/en-us/library/…
  • 是的,这里的问题是序列是异构的,所以我们需要找到一个“共同点”
  • 优秀。我一直在寻找使用 except() 但如果你对这两个类没有共同点,你似乎不能使用它。我想知道您为什么选择使用 LookUp?
【解决方案2】:

不是Except,而是正确的结果和类似的性能:

// assumes that the Id property is an Int32
var tempKeys = new HashSet<int>(list2.Select(x => x.Id));
var listdiff = list1.Where(x => tempKeys.Add(x.Id));

当然,您可以将其全部封装在您自己的可重复使用的扩展方法中:

var listdiff = list1.Except(list2, x => x.Id, y => y.Id);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<TFirst> Except<TFirst, TSecond, TKey>(
        this IEnumerable<TFirst> first,
        IEnumerable<TSecond> second,
        Func<TFirst, TKey> firstKeySelector,
        Func<TSecond, TKey> secondKeySelector)
    {
        // argument null checking etc omitted for brevity

        var keys = new HashSet<TKey>(second.Select(secondKeySelector));
        return first.Where(x => keys.Add(firstKeySelector(x)));
    }
}

【讨论】:

    【解决方案3】:

    简而言之,将列表设为 List&lt;object&gt; 并使用 .NET 4.0 中的 C# 功能:dynamic

    例子:

    var listDiff = list1
        .AsEnumerable<object>()
        .Except(list2
            .AsEnumerable<object>()
            .Select(row => ((dynamic)row).ID));
    

    【讨论】:

    • 抱歉忘记补充我使用的是 v3.5
    【解决方案4】:

    如果您只希望list1 中的Ids 不在list2 中,您可以这样做:

    var idsInList1NotInList2 = list1.Select(user1 => user1.Id)
                                    .Except(list2.Select(user2 => user2.Id));
    

    如果您也需要关联的 User1 对象,这是一种方法(假设 ID 对于 User1 对象是唯一的):

    // Create lookup from Id to the associated User1 object
    var user1sById = list1.ToDictionary(user1 => user1.Id);
    
    // Find Ids from the lookup that are not present for User2s from list2
    // and then retrieve their associated User1s from the lookup
    var user1sNotInList2 = user1sById.Keys
                                     .Except(list2.Select(user2 => user2.Id))
                                     .Select(key => user1sById[key]);
    

    编辑:vc74 对这个想法的理解要好一些;它不需要唯一性。

    【讨论】:

      【解决方案5】:
      public static IEnumerable<TSource> Except<TSource, CSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> TSelector, IEnumerable<CSource> csource, Func<CSource, TKey> CSelector)
          {
              bool EqualFlag = false;
              foreach (var s in source)
              {
                  EqualFlag = false;
                  foreach (var c in csource)
                  {
                      var svalue = TSelector(s);
                      var cvalue = CSelector(c);
                      if (svalue != null)
                      {
      
                          if (svalue.Equals(cvalue))
                          {
                              EqualFlag = true;
                              break;
                          }
                      }
                      else if (svalue == null && cvalue == null)
                      {
                          EqualFlag = true;
                          break;
                      }
                  }
                  if (EqualFlag)
                      continue;
                  else
                  {
                      yield return s;
                  }
              }
      
          }
      

      【讨论】:

        【解决方案6】:

        试试

        list1.Where(user1 => !list2.Any(user2 => user2.Id.Equal(user1.Id)));
        

        【讨论】:

        • 欢迎来到Stack Overflow!为什么这行得通?那些可以自己回答这个问题的人可能不需要别人的代码。
        猜你喜欢
        • 2021-09-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-28
        • 2021-10-17
        • 2013-02-18
        • 2012-04-03
        • 1970-01-01
        相关资源
        最近更新 更多