UPDATE

The purpose of this method is not to quickly get the count of a collection - it is to let the developer know if it possible to cheaply obtain the count. If it isn’t, it will return a status so that the developer can know how to proceed next.

A fairly common operation is determining the length of a collection, for a myriad of reasons - loop control, buffer space allocation etc.

There is a way to determine this - the Count() method of the Enumerable class.

How this works internally depends on the underlying collection.

If it is an IColleciton, there is a Count property that already has this information, so that is returned immediately.

However there are cases where the runtime will actually have to enumerate the collection to determine the number of items.

It is for this reason that a new method has been introduced to try and quickly establish the count. If it is possible, the method returns true and returns the count as an out variable.

If it is not possible the method returns false and a zero as the count in the out variable.

The method is named TryGetNonEnumeratedCount

You invoke it as follows:

var success = collection.TryGetNonEnumeratedCount(out var count);
if (success)
{
    // We succeeded!
    Console.WriteLine($"The collection has {count} items");
}
else
{
    // We failed    
}

NOTE: This method will return a count of 0 if it is unable to quickly get a count of the collection.

You should therefore make it a habit wrap it around harness like this:

if (!enumerable.TryGetNonEnumeratedCount(out var count))
{
	// do your slow count here
}

For the tests it just so happens that Enumerable.Range has been optimized such that this method works for this use case.

However if you modify the range (perhaps with a Where filter, the method returns false)

I have attempted to verify the speed of this with some tests:

public class Test
{
    IEnumerable<int> enumerable;
    List<int> list;
    int[] array;

    public Test()
    {
        enumerable = Enumerable.Range(0, 25);
        list = enumerable.ToList();
        array = enumerable.ToArray();
    }

    [Benchmark(Baseline = true)]
    [BenchmarkCategory("Enumerable")]
    public int GetEnumerableCountOld()
    {
        var count = enumerable.Count();
        return count;
    }

    [Benchmark]
    [BenchmarkCategory("Enumerable")]
    public int GetEnumerableCountNew()
    {
        enumerable.TryGetNonEnumeratedCount(out var count);
        return count;
    }

    [Benchmark(Baseline = true)]
    [BenchmarkCategory("List")]
    public int GetListCountOld()
    {
        var count = list.Count();
        return count;
    }

    [Benchmark]
    [BenchmarkCategory("List")]
    public int GetListCountNew()
    {
        list.TryGetNonEnumeratedCount(out var count);
        return count;
    }

    [Benchmark(Baseline = true)]
    [BenchmarkCategory("Array")]
    public int GetArrayCountOld()
    {
        var count = array.Count();
        return count;
    }
    [Benchmark]
    [BenchmarkCategory("Array")]
    public int GetArrayCountNew()
    {
        array.TryGetNonEnumeratedCount(out var count);
        return count;
    }
}

My results have been largely inconclusive as to whether in fact this method is any better than Enumerable.Count()

My first run got this:

It was actually SLOWER for Enumerables (by 106%) and Arrays (by 13%). Lists performed better, (by 30%).

My second run got this:

Here Lists and Arrays performed slower, and Enumerables were only marginally better.

My third run got this:

All were slower!

My final run got this:

Again, all were slower.

Thoughts

This is be a good addition to the developer toolbelt to allow checking if the determination of the size of a collection will be a cheap operation. If it is - the count will be returned.

If it isn’t, the developer can decide what to do; including getting the count anyway the hard way.

The code for the tests is in my Github. I encourage you to run them for yourself and comment your results.

You run the benchmarks by executing this command in the folder with the code:

dotnet run -c Release

You must run it in the Release configuration for the metrics to be captured correctly.

Also if there is an issue with the setup of the tests, you can send a pull request.

TLDR

There is a TryGetNonEnumeratedCount method that should allow you to verify if you can cheaply determine the size of a collection.

This is Day 12 of the 30 Days Of .NET 6 where every day I will attempt to explain one new / improved thing in the upcoming release of .NET 6.

Happy hacking!