Foreach循环正确-使用.Any()检查返回错误

时间:2019-06-14 08:19:29

标签: c# linq

我有一个Guid列表,其中保留了我的项目列表中所有重复的Guid

IEnumerable<Guid> duplicateExists = ListOfMyItems
  .GroupBy(x => x.ID)
  .Where(group => group.Count() > 1)
  .Select(group => group.Key);

现在,我要检查是否存在重复项,如果存在,请从ListOfMyItems中删除最后一次出现的重复项。 DeleteItemFromOrder只是从数据库中删除一行。

这有效:

 foreach (var item in duplicateExists)
 {                        
     _ = ListOfMyItems.Remove(ListOfMyItems.Last(x => x.ID == item));
     MyLogic.DeleteItemFromOrder(Bill.ID, item);                      
     return;
 }

这将输入条件if,然后在DeleteItemFromOrder上失败,告诉我duplicateExists - sequence contains no elements

 if(duplicateExists.Any())
 {                        
     _ = ListOfMyItems.Remove(ListOfMyItems.Last(x => x.ID == duplicateExists.First()));
     MyLogic.DeleteItemFromOrder(Bill.ID, duplicateExists.First());                      
     return;
 } 

第二个示例为什么失败?

3 个答案:

答案 0 :(得分:7)

这是 Linq 延期执行的结果:当您放置

 IEnumerable<Guid> duplicateExists = ListOfMyItems
   .GroupBy(x => x.ID)
   .Where(group => group.Count() > 1)
   .Select(group => group.Key);

系统实际上不执行查询;那么你有

if (duplicateExists.Any())
{                        
    _ = ListOfMyItems.Remove(ListOfMyItems.Last(x => x.ID == duplicateExists.First()));
    MyLogic.DeleteItemFromOrder(Bill.ID, duplicateExists.First());                      
    return;
} 

系统将执行以下操作:

  1. 执行duplicateExists.Any()并获得true,因为存在重复项
  2. 执行duplicateExists.First()获取重复项,并从ListOfMyItems-ListOfMyItems.Remove
  3. 中删除
  4. 尝试对duplicateExists.First()再次执行MyLogic.DeleteItemFromOrder,由于<{> 1 已修改ListOfMyItems并且没有重复-duplicateExists

快速补丁具体化 duplicateExists force ,系统执行查询并将结果存储在集合中,例如数组:

  IEnumerable<Guid> duplicateExists = ListOfMyItems
   .GroupBy(x => x.ID)
   .Where(group => group.Count() > 1)
   .Select(group => group.Key)
   .ToArray();

答案 1 :(得分:2)

尽管第一个代码片段可能有效,但它们都让人感到恐惧。

第一个只能工作,因为GroupBy执行隐式的ToList迭代。

对IEnumerable进行多次迭代需要一些预防措施。 Linq是惰性计算的,两次调用duplicateExists意味着它再次被执行。 ToList()可以阻止它。

通过以下方式,您将获得更高的安全性和更好的性能:

IEnumerable<Guid> duplicateExists = ListOfMyItems
  .GroupBy(x => x.ID)
  .Where(group => group.Count() > 1)
  .Select(group => group.Key)
  .ToList();  // cache the results

关于失败:

if(duplicateExists.Any())
 {                        
     // remove item X
     _ = ListOfMyItems.Remove(ListOfMyItems.Last(x => x.ID == duplicateExists.First()));

      // trying to find X again but duplicateExists is re-evaluated
     MyLogic.DeleteItemFromOrder(Bill.ID, duplicateExists.First());                      
     return;
 } 

答案 2 :(得分:2)

LINQ使用惰性评估。您需要先将初始查询解析为物理列表,然后才能使用它来修改原始数据集,如下所示:

var duplicateExists = ListOfMyItems
  .GroupBy(x => x.ID)
  .Where(group => group.Count() > 1)
  .Select(group => group.Key)
  .ToArray();