任何()停止在成功

  • 本文关键字:成功 任何 c# linq
  • 更新时间 :
  • 英文 :


更具体地说:Linq扩展方法Any(IEnumerable collection, Func predicate)将停止检查集合的所有剩余元素,一旦谓词为项产生true ?

因为我不想花太多时间去弄清楚我是否需要做那些真正昂贵的部分:

if(lotsOfItems.Any(x => x.ID == target.ID))
    //do expensive calculation here

因此,如果Any总是检查源中的所有项目,这可能最终会浪费时间,而不是直接使用:

var candidate = lotsOfItems.FirstOrDefault(x => x.ID == target.ID)
if(candicate != null)
     //do expensive calculation here

因为我很确定FirstOrDefault一旦得到结果就返回,并且如果在集合中没有找到合适的条目,则只继续遍历整个Enumerable

有没有人有关于Any内部工作的信息,或者有人可以为这种决定提出解决方案吗?

另外,一位同事建议这样做:

if(!lotsOfItems.All(x => x.ID != target.ID))

因为这应该在条件第一次返回false时停止,但我不确定这一点,所以如果有人能对此有所了解,我将不胜感激。

正如我们从源代码中看到的,Yes:

 internal static bool Any<T>(this IEnumerable<T> source, Func<T, bool> predicate) {
            foreach (T element in source) {
                if (predicate(element)) {
                    return true; // Attention to this line
                }
            }
            return false;
        }

Any()是确定序列中任意元素是否满足LINQ条件的最有效方法。

also:一个同事建议用

如果(! lotsOfItems

。所有(x => x.ID != target.ID)),因为这应该是一旦条件第一次返回false,停止,但我不是当然,如果有人能解释一下这个问题非常感谢:>]

All()判断一个序列的所有元素是否满足一个条件。因此,一旦可以确定结果,就停止对source的枚举。

额外注意:
如果您正在使用Linq to对象,则上述情况是正确的。如果您正在使用Linq to Database,那么它将创建一个查询,并将对数据库执行查询。

您可以自己测试:https://ideone.com/nIDKxr

public static IEnumerable<int> Tester()
{
    yield return 1;
    yield return 2;
    throw new Exception();
}
static void Main(string[] args)
{
    Console.WriteLine(Tester().Any(x => x == 1));
    Console.WriteLine(Tester().Any(x => x == 2));
    
    try
    {
        Console.WriteLine(Tester().Any(x => x == 3));
    }
    catch
    {
        Console.WriteLine("Error here");
    }
}

是的,它有:-)

also:一个同事建议用

如果(! lotsOfItems

。所有(x =比;= target.ID))

因为这应该在条件第一次返回false时停止,但我不确定,所以如果有人能对此有所了解,我将不胜感激:>]

使用相同的推理,即使其中一个元素返回false, All()也可以继续:-)不,甚至All()也被正确编程:-)

它做任何最快的方法来做它必须做的事情。

当在IEnumerable上使用时,将沿着以下行:

foreach(var item in source)
  if(predicate(item))
    return true;
return false;

或者对于不带谓词的变体:

using(var en = source.GetEnumerator())
  return en.MoveNext();

当在数据库上运行时,它会像

一样
SELECT EXISTS(SELECT null FROM [some table] WHERE [some where clause])

以此类推。执行的方式又取决于WHERE子句可用的索引,因此可以是快速索引查找,在找到第一个匹配项时终止全表扫描,或者在找到第一个匹配项时终止索引查找,然后在找到第一个匹配项时终止部分表扫描,这取决于。

其他的Linq提供者也会有其他的实现,但通常负责的人会试图至少是合理的效率。

总之,你可以相信它至少比调用FirstOrDefault更有效,因为FirstOrDefault使用类似的方法,但必须返回一个完整的对象(可能是构造它)。同样,根据这个答案,!All(inversePredicate)倾向于与Any(predicate)相当。

Single是这个

的例外

更新:以下内容从现在起不再适用于。net Core,它已经改变了Single的实现。

需要注意的是,在link -to对象的情况下,SingleSingleOrDefault的重载(使用谓词)不会在确定失败时停止。虽然Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)的明显方法是这样的:

public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
  /* do null checks */
  using(var en = source.GetEnumerator())
    while(en.MoveNext())
    {
      var val = en.Current;
      if(predicate(val))
      {
        while(en.MoveNext())
          if(predicate(en.Current))
            throw new InvalidOperationException("too many matching items");
        return val;
      }
    }
  throw new InvalidOperationException("no matching items");
}

实际的实现是这样的:

public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    /* do null checks */
    var result = default(TSource);
    long tally = 0;
    for(var item in source)
        if(predicate(item))
        {
            result = item;
            checked{++tally;}
        }
    switch(tally)
    {
        case 0:
            throw new InvalidOperationException("no matching items");
        case 1:
            return result;
        default:
            throw new InvalidOperationException("too many matching items");
    }
}

现在,虽然成功的Single将不得不扫描所有内容,这可能意味着不成功的Single比它需要的要慢得多(甚至可能抛出一个未记录的错误),如果意外重复的原因是将项目复制到序列中的错误-因此使其远远大于它应该是,那么应该帮助您发现问题的Single现在正在拖走这个。

SingleOrDefault也有同样的问题。

这只适用于linq-to-object,但.Where(predicate).Single()Single(predicate)更安全。

Any在第一次匹配时停止。

我不知道文档是否保证了这一点,但由于兼容性原因,这种行为现在一直有效地修复了。

是,它在满足谓词一次时停止。下面是通过RedGate反射器的代码:

    [__DynamicallyInvokable]
    public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        if (source == null)
        {
            throw Error.ArgumentNull("source");
        }
        if (predicate == null)
        {
            throw Error.ArgumentNull("predicate");
        }
        foreach (TSource local in source)
        {
            if (predicate(local))
            {
                return true;
            }
        }
        return false;
    }

相关内容

  • 没有找到相关文章

最新更新