Bad implementation of Enumerable.Single?

后端 未结 7 542
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-15 03:19

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

public static TSource Single(this IEnumerable source, Func<         


        
相关标签:
7条回答
  • 2020-12-15 04:06

    Update:
    I got some very good feedback to my answer, which has made me re-think. Thus I will first provide the answer that states my "new" point of view; you can still find my original answer just below. Make sure to read the comments in-between to understand where my first answer misses the point.

    New answer:

    Let's assume that Single should throw an exception when it's pre-condition is not met; that is, when Single detects than either none, or more than one item in the collection matches the predicate.

    Single can only succeed without throwing an exception by going through the whole collection. It has to make sure that there is exactly one matching item, so it will have to check all items in the collection.

    This means that throwing an exception early (as soon as it finds a second matching item) is essentially an optimization that you can only benefit from when Single's pre-condition cannot be met and when it will throw an exception.

    As user CodeInChaos says clearly in a comment below, the optimization wouldn't be wrong, but it is meaningless, because one usually introduces optimizations that will benefit correctly-working code, not optimizations that will benefit malfunctioning code.

    Thus, it is actually correct that Single could throw an exception early; but it doesn't have to, because there's practically no added benefit.


    Old answer:

    I cannot give a technical reason why that method is implemented the way it is, since I didn't implement it. But I can state my understanding of the Single operator's purpose, and from there draw my personal conclusion that it is indeed badly implemented:

    My understanding of Single:

    What is the purpose of Single, and how is it different from e.g. First or Last?

    Using the Single operator basically expresses one's assumption that exactly one item must be returned from the collection:

    • If you don't specify a predicate, it should mean that the collection is expected to contain exactly one item.

    • If you do specify a predicate, it should mean that exactly one item in the collection is expected to satisfy that condition. (Using a predicate should have the same effect as items.Where(predicate).Single().)

    This is what makes Single different from other operators such as First, Last, or Take(1). None of those operators have the requirement that there should be exactly one (matching) item.

    When should Single throw an exception?

    Basically, when it finds that your assumption was wrong; i.e. when the underlying collection does not yield exactly one (matching) item. That is, when there are zero or more than one items.

    When should Single be used?

    The use of Single is appropriate when your program's logic can guarantee that the collection will yield exactly one item, and one item only. If an exception gets thrown, that should mean that your program's logic contains a bug.

    If you process "unreliable" collections, such as I/O input, you should first validate the input before you pass it to Single. Single, together with an exception catch block, is not appropriate for making sure that the collection has only one matching item. By the time you invoke Single, you should already have made sure that there'll be only one matching item.

    Conclusion:

    The above states my understanding of the Single LINQ operator. If you follow and agree with this understanding, you should come to the conclusion that Single ought to throw an exception as early as possible. There is no reason to wait until the end of the (possibly very large) collection, because the pre-condition of Single is violated as soon as it detects a second (matching) item in the collection.

    0 讨论(0)
提交回复
热议问题