2

The C# IEnumerable.Count(IEnumerable<TSource>) and IEnumerable.Count(IEnumerable<TSource>, Func<TSource,Boolean>) both return type int implying it can return a value less than zero. It doesn't make sense for it to do so, but if the type is int it's theoretically possible for the result to be a negative value.

IEnumerable<string> list = GetMyList(); 
int listCount = list.Count(); 

// is it more correct to do this: 
if(listCount <= 0)
{
    DoSomething(); 
} 
else 
{
    DoSomethingElse(); 
} 

// or this: 
if(listCount == 0)
{
    DoSomething(); 
} 
else if (listCount > 0)
{
    DoSomethingElse(); 
} 
else 
{
    // but this branch will never be hit 
    throw new Exception(); 
}

I can't find any information online about whether or not that can actually happen, and the Documentation for Enumerable.Count does not specify any cases in which it might.

Just wondering if anyone has any experience with this happening or any information on this.

Thanks

  • 5
    Related: [Why does .NET use `int` instead of `uint` in certain classes?](https://stackoverflow.com/q/782629/150605), [Why is `Array.Length` an `int`, and not an `uint`](https://stackoverflow.com/q/6301/150605) – Lance U. Matthews Mar 27 '18 at 00:15
  • Could it return a negative? Yes. Would it ever return a negative? *Very* unlikely. It's possible that there's some implementation of `IEnumerable` where it makes sense but I can't think of where it would be useful. Perhaps in some streaming context like reactive extensions. – DavidG Mar 27 '18 at 00:24
  • Interesting - https://math.stackexchange.com/questions/2035875/can-you-have-negative-sets –  Mar 27 '18 at 00:29
  • 3
    How would you interpret a negative number for ["Returns the number of elements in a sequence."](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.count?view=netframework-4.7.1#System_Linq_Enumerable_Count__1_System_Collections_Generic_IEnumerable___0__) as anything other than a bug? – Tom Blodget Mar 27 '18 at 00:40
  • 2
    Only way that happens is if you have something that implements `ICollection` that returns a negative value for it's `Count` property as `Enumerable.Count` will use that if it can. Otherwise it attempts to iteratethe `IEnumerable` and counts from 0 the number of items. – juharr Mar 27 '18 at 01:05
  • 1
    I worry about this sometimes. Perhaps a short course in Common Sense is called for. –  Mar 27 '18 at 13:38
  • So, again, how would you interpret a negative number for ["Gets the number of elements contained in the ICollection"](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.icollection-1.count?view=netframework-4.7.1) as anything other than a bug? An interface specification carries more of a contract than the compiler-enforced signature. – Tom Blodget Mar 27 '18 at 23:12

2 Answers2

3

The purpose of the return data type is not to imply a range of numbers. Although it naturally does set a hard upper and lower limit (sort of... see LongCount), that is just a side effect of type compatibility and generalizability.

Consider the Array's Rank property. The maximum value is 32. But we don't store it in a byte or short. We store it in an int. Why? We don't need all that range. But it's recommended: they're fast (they align well to register size and memory maps) and, by convention, It is easier to work with other libraries if you work with ints. Also, the int datatype is CLS-compliant (meaning that any language that implements the CLR must support it) but uint32 is not.

Returning a numeric data type that has a particular range in no way implies that the full range will be used. And returning a negative value from IEnumerable.Count() would not only be poor form, but it would be semantically incorrect, as a count must obviously return a cardinal number, which must be and integer and non-negative.

John Wu
  • 50,556
  • 8
  • 44
  • 80
2

Actually there´s no such method Count defined on neither IEnumerable nor IEnumerable<T>, but on the static class System.Linq.Enumerable. As it´s a (static) extension-method, you can´t override nor modify this at all. So let´s look into the extension-method:

public static int Count<TSource>(this IEnumerable<TSource> source)
{
    if (source == null) throw Error.ArgumentNull("source");
    ICollection<TSource> collectionoft = source as ICollection<TSource>;
    if (collectionoft != null) return collectionoft.Count;
    ICollection collection = source as ICollection;
    if (collection != null) return collection.Count;

    int count = 0;
    using (IEnumerator<TSource> e = source.GetEnumerator()) {
        checked {
            while (e.MoveNext()) count++;
        }
    }
    return count;
}

As you can see the only way Count will ever return a negative number is by implelementing ICollection.Count, which is called by Enumerable.Count() (as you can see above), or by creating your own extension-method with the exact same name and relying on extension-method resolution in order to "hide" the method from System.Linq.Enumerable:

public static class MyClass
{
    public static int Count(this IEnumerable<T> src) { return -1; }
}

However I can´t see any reason why one should do this at all.

So in short the method can return a negative number. Doing this however breaks the principle of least astonishment and thus is a bad idea.

MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111
  • _"So the method may never return a negative number."_ - It returns an Int which last time I checked is **signed** –  Mar 27 '18 at 00:30
  • You're just showing the extensions of `IEnumerable`, its entirely possible a third party library has implemented it's own count that does return a negative number. – DavidG Mar 27 '18 at 00:32
  • @DavidG `Count` isn´t even defined on the interface itself, only on the static class `Enumerable`. So the only way that´ll work is by creating your own extension-method for this and *not* including `System.Linq`, or do I miss anything? – MakePeaceGreatAgain Mar 27 '18 at 00:35
  • 4
    I'm just saying it's not so simple. For example, the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.count?view=netframework-4.7.1#System_Linq_Enumerable_Count__1_System_Collections_Generic_IEnumerable___0__) state: *If the type of source implements ICollection, that implementation is used to obtain the count of elements. Otherwise, this method determines the count.* – DavidG Mar 27 '18 at 00:40
  • For example, [see here](https://github.com/Microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs#L1305) which is the code tht determines what method to call to get a count. That could be a custom `ICollection` implementation. – DavidG Mar 27 '18 at 00:47
  • Perhaps some demo code would explain it better https://dotnetfiddle.net/9od6Si This shows you don't need to "hide" anything as you say. – DavidG Mar 27 '18 at 01:07
  • You don't even need to hack with collection or not including LINQ if you just concerned about `.Count()` returning -1 (and not `IEnumerable.Count` in particular) - non-generic `Count` extension method or method of custom type will be preferred over generic one... `static int Count(this MyEnumerableType t) { return -42;}` – Alexei Levenkov Mar 27 '18 at 01:15
  • This is a prickly one to vote on; the difference between what a method _can_ return and what it (likely) _should_ return. Considering the two ways to make `Count()` return a negative number are both somewhat evil, I give @HimBromBeere the benefit of the doubt and vote +0. Still, the answer to "Can `IEnumerable.Count()` return a negative number?" (that is, "Can `IEnumerable.Count()` _be made to_ return a negative number?") is clearly "Yes". – Lance U. Matthews Mar 27 '18 at 01:19
  • If you go to all the effort of creating a replacement/preferred `Count()` method, we're not really talking about the same `IEnumerable.Count(IEnumerable)` anymore though, are we? – Zac Faragher Mar 27 '18 at 01:21
  • @ZacFaragher Yes we are because the `ICollection` implementation of it gets called internally. – DavidG Mar 27 '18 at 01:22
  • I got down-voted for saying "no, it doesn't return a negative number", for not factoring in someone hacking and making it do so. I felt the question was more about why uint is not used to enforce a positive count. – ForeverZer0 Mar 27 '18 at 06:12
  • 1
    @ForeverZer0 Actually you got downvoted for your answer being mostly incorrect. – DavidG Mar 27 '18 at 09:19
  • @DavidG However this assumes we *implement* `ICollection`. For other enumerables we can´t modify the behaviour at all (unless we rely on the hack of "hiding" I mentioned earlier). – MakePeaceGreatAgain Mar 27 '18 at 13:36
  • But you're assuming it *isn't* implemented. The point is that it could be and because we're talking about an interface, not a concrete class, we can't be sure what it contains. For example, what would `Count` return for something like a network stream? In theory it would either never return or (more sensibly) throw an error. We often get too caught up in thinking about enumerables as finite lists of things but in reality they can be all sort of weird and wonderful things. – DavidG Mar 27 '18 at 13:41
  • @DavidG If all we have is an `IEnumerable` I can´t see any way around the extension-method apart from those mentioned above. So even if your own implementation for `IEumerable` has its own `Count`-method, it´ll never be called, as seen in [my fiddle](https://dotnetfiddle.net/jQNWqC). Instead the extension-method is called which depends on if `ICollection` is also implemented. – MakePeaceGreatAgain Mar 27 '18 at 14:32
  • @ForeverZer0 I understood you to be referring to [CLS-compliance](https://learn.microsoft.com/en-us/dotnet/standard/language-independence-and-language-independent-components). – Tom Blodget Mar 27 '18 at 23:08