坏实施Enumerable.Single的? [英] Bad implementation of Enumerable.Single?

查看:239
本文介绍了坏实施Enumerable.Single的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在Enumerable.cs碰到这个实施反射。

I came across this implementation in Enumerable.cs by reflector.

public static TSource Single<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    //check parameters
    TSource local = default(TSource);
    long num = 0L;
    foreach (TSource local2 in source)
    {
        if (predicate(local2))
        {
            local = local2;
            num += 1L;
            //I think they should do something here like:
            //if (num >= 2L) throw Error.MoreThanOneMatch();
            //no necessary to continue
        }
    }
    //return different results by num's value
}

我觉得他们应该打破循环,如果有超过2项符合条件,为什么他们总是遍历整个集合?在这情况下,反射器的反汇编DLL错误,我写了一个简单的测试:

I think they should break the loop if there are more than 2 items meets the condition, why they always loop through the whole collection? In case of that reflector disassembles the dll incorrectly, I write a simple test:

class DataItem
{
   private int _num;
   public DataItem(int num)
   {
      _num = num;
   }

   public int Num
   {
      get{ Console.WriteLine("getting "+_num); return _num;}
   }
} 
var source = Enumerable.Range(1,10).Select( x => new DataItem(x));
var result = source.Single(x => x.Num < 5);

有关本次测试的情况下,我认为它会打印出越来越0,得到1,然后抛出异常。但事实是,它让渐0 ......让10,并抛出一个异常。 有他们实现这个方法任何这样的算法的原因是什么?

For this test case, I think it will print "getting 0, getting 1" and then throw an exception. But the truth is, it keeps "getting 0... getting 10" and throws an exception. Is there any algorithmic reason they implement this method like this?

修改你们当中有些人认为这是因为的的predicate EX pression副作用的,深刻的思想和一些测试用例后,我有一个结论,即的副作用并不在这种情况下没关系。请提供一个例子,如果你不同意这个结论。

EDIT Some of you thought it's because of side effects of the predicate expression, after a deep thought and some test cases, I have a conclusion that side effects doesn't matter in this case. Please provide an example if you disagree with this conclusion.

推荐答案

是的,我也觉得有点奇怪尤其是因为这并不需要predicate过载(即适用于刚刚序列)的确实的似乎有快速掷优化。

Yes, I do find it slightly strange especially because the overload that doesn't take a predicate (i.e. works on just the sequence) does seem to have the quick-throw 'optimization'.

然而,在首创置业的防守,我会说,在 InvalidOperation异常的单抛出一个的boneheaded例外不应通常被用于控制流量。这是没有必要为这样的情况下,以由库进行优化。

In the BCL's defence however, I would say that the InvalidOperation exception that Single throws is a boneheaded exception that shouldn't normally be used for control-flow. It's not necessary for such cases to be optimized by the library.

使用code ,其中零个或多个匹配是一个完美的有效的可能性,如:

Code that uses Single where zero or multiple matches is a perfectly valid possibility, such as:

try
{
     var item = source.Single(predicate);
     DoSomething(item);
}

catch(InvalidOperationException)
{
     DoSomethingElseUnexceptional();    
}

应该重构为code,它的的使用异常的控制流,例如(只是一个范例,这样可以更有效地实现):

should be refactored to code that doesn't use the exception for control-flow, such as (only a sample; this can be implemented more efficiently):

var firstTwo = source.Where(predicate).Take(2).ToArray();

if(firstTwo.Length == 1) 
{
    // Note that this won't fail. If it does, this code has a bug.
    DoSomething(firstTwo.Single()); 
}
else
{
    DoSomethingElseUnexceptional();
}


在换句话说,我们应该把使用的案件时,我们预计该序列包含的只有的一场比赛。它应该行为与首先但与其他运行时断言,该序列不包含多个匹配。像任何其他的断言,失败,即情况下,当抛出,应该被用来重新在程序present错误(无论是在运行查询或传递给它的调用者的参数)方法。


In other words, we should leave the use of Single to cases when we expect the sequence to contain only one match. It should behave identically to Firstbut with the additional run-time assertion that the sequence doesn't contain multiple matches. Like any other assertion, failure, i.e. cases when Single throws, should be used to represent bugs in the program (either in the method running the query or in the arguments passed to it by the caller).

这给我们留下了两种情况:

This leaves us with two cases:

  1. 的说法认为:有一个匹配。在这种情况下,我们希望消耗的整个序列的反正的主张索赔。有没有好处的优化。事实上,人们可以说,在优化的OP所提供的样本实现实际上是因为在循环的每次迭代检查的要慢。
  2. 断言失败:有零个或多个匹配。在这种情况下,我们的的后面扔,比我们的可以的,但这不是因为异常愚蠢的这样一个大问题:这是指示错误的必须是固定的。
  1. The assertion holds: There is a single match. In this case, we want Single to consume the entire sequence anyway to assert our claim. There's no benefit to the 'optimization'. In fact, one could argue that the sample implementation of the 'optimization' provided by the OP will actually be slower because of the check on every iteration of the loop.
  2. The assertion fails: There are zero or multiple matches. In this case, we do throw later than we could, but this isn't such a big deal since the exception is boneheaded: it is indicative of a bug that must be fixed.

综上所述,如果执行不力是咬你性能方​​面的生产,无论是:

To sum up, if the 'poor implementation' is biting you performance-wise in production, either:

  1. 您正在使用不正确。
  2. 您在的的程序中的错误。一旦错误是固定的,这个特殊的性能问题就会迎刃而解。
  1. You are using Single incorrectly.
  2. You have a bug in your program. Once the bug is fixed, this particular performance problem will go away.

编辑:澄清我的观点

编辑:这是一个的有效的使用单中,一旦出现故障指示错误在呼叫的code(坏参数):

Here's a valid use of Single, where failure indicates bugs in the calling code (bad argument):

public static User GetUserById(this IEnumerable<User> users, string id)
{
     if(users == null)
        throw new ArgumentNullException("users");

     // Perfectly fine if documented that a failure in the query
     // is treated as an exceptional circumstance. Caller's job 
     // to guarantee pre-condition.        
     return users.Single(user => user.Id == id);    
}

这篇关于坏实施Enumerable.Single的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆