When dealing with collections it isn't uncommon that one needs to be able to get the count of an IEnumerable<T> as a default value for a new collection's capacity. The naive thing to do would be to simply use the Enumerable.Count extension method however if the IEnumerable<T> isn't an ICollection<T> or any of the other interfaces or types with a Count or Length property that method results in enumerating the collection which isn't desirable when just wanting a default value for a new collection's capacity. Instead of manually checking for those various interfaces each time someone needs this functionality I would like a non-enumerating count linq method added.
namespace System.Linq {
public static class Enumerable {
+ public static bool TryGetCount(IEnumerable<T> source, out int count);
}
}
It would essentially retrieve the count so long as it doesn't necessitate enumerating the collection.
This would be a somewhat fragile method in that it should be updated whenever the next interface or type with a Count or Length property is introduced and thus would exhibit different behavior between versions.
Count with an allowEnumeration boolean parameter.TryGetCount.Alternatively we could add a public static int? Count(this IEnumerable<TSource> source, bool onlyIfCheap) overload similar to the internal IIlistProvider.GetCount(bool onlyIfCheap) method but it would return null if onlyIfCheap is true and it can't be determined in a O(1) operation.
Alternatively we could add a public static int? Count(this IEnumerable
source, bool onlyIfCheap)
Don't like this approach. The onlyIfCheap is not a quantifiable term. What is cheap to me may not be cheap to you.
It would essentially have the same body as the current Count extension method but would return the defaultValue parameter if it would need to enumerate the collection to find the count.
There are plenty of examples today where there is a pair of methods Operation and OperationOrDefault and they have a well established pattern. When Operation fails do the OrDefault action. The problem is that Count cannot fail. This proposal would be both adding a new method and a failure mode for something that can't fail today.
If we were to add an API like this I tihnk it would need to be a pair of methods:
OrDefault variant@cston, @333fred
@jaredpar
So you would prefer something like the following?
namespace System.Linq {
public static class Enumerable {
+ public static int? NonEnumeratingCount(this IEnumerable<TSource> source);
+ public static int NonEnumeratingCountOrDefault(this IEnumerable<TSource> source, int defaultValue);
}
}
I'm not sure an OrDefault method is necessary as it's very simple to achieve without, at least in C#.
C#
var capacity = source.NonEnumeratingCount() ?? 0;
Instead of onlyIfCheap we could rename it to allowEnumeration which would have the reverse connotation.
What's wrong with this?
int? QuickCount<T>(this IEnumerable<T> source ) => (source as IReadOnlyCollection<T>)?.Count;
The only purpose of IReadOnlyCollection<T> is to tell us when the Count property is available. So its pretty safe to rely on it.
Or
bool TryCount<T>(this IEnumerable<T> source, out int count)
馃
@Grauenwolf I guess my main motivation is to support IEnumerables that are the result of LINQ methods which don't currently implement IReadOnlyCollection<T>.
I've created the new issue dotnet/corefx#35173 to address adding implicit support for IReadOnlyCollection<T> to the results of LINQ methods when possible. If that were implemented there'd be less motivation to me to add this non-enumerating count method.
Ad-hoc implementations that try to calculate the count without enumerating are pretty common, so I think it would make sense to expose a method that does it in a correct and complete way. That being said, I don't think it should be called Count and it probably shouldn't be an extension method either. How about something like
public static class Enumerable
{
public static bool TryGetCount(IEnumerable<T> source, out int count);
}
I agree it shouldn't be an extension method. I like the TryGetCount method name, works for me.
@TylerBrinkley would you be able to update the original post? I'll see if I can submit this for review.
@eiriktsarpalis I've updated the original post.
I think it will cause developers confused when returning false...
C#
Enumerable.TryGetCount(mySource, out int count)
...method results in enumerating the collection which isn't desirable when just wanting a default value for a new collection's capacity.
I know it's reasonable but I prefer to handle it by users themself. Or, public static int NonEnumeratingCountOrDefault(this IEnumerable<TSource> source, int defaultValue = default); should be better. At least it informs users what this method does and users won't have to understand the actual meaning of "Try" then deal with failure result.
I think it will cause developers confused when returning false
How so? The point of this addition is to provide its user a clear signal as to whether a count can be obtained without enumerating. Roughly it would translate into the following code:
if (Enumerable.TryGetCount(source, out int count))
{
T[] fixedBuffer = new T[count];
ConsumeUsingFixedBuffer(source, fixedBuffer);
}
else
{
List<T> resizingBuffer = new List<T>();
ConsumeUsingResizingBuffer(source, resizingBuffer);
}
At least it informs users what this method does and users won't have to understand the actual meaning of "Try" then deal with failure result.
I think you are right in pointing out that the currently proposed name TryGetCount is ambiguous in what it is doing. How about TryGetNonEnumeratedCount?
Well, it's better.
Most helpful comment
Alternatively we could add a
public static int? Count(this IEnumerable<TSource> source, bool onlyIfCheap)overload similar to the internalIIlistProvider.GetCount(bool onlyIfCheap)method but it would returnnullifonlyIfCheapistrueand it can't be determined in a O(1) operation.