Handle the collection of data it`s not what you think. Let`s handle it efficiently.
Intro
In .NET, Enumerable.Empty<T>()
is a method provided by the System.Linq
namespace that returns an empty sequence of a specified type. This is useful when you need to work with an IEnumerable<T>
that has no elements, such as when initializing or chaining queries.
First time with BenchmarkDotNet. Please refer to this blog Post for beginner guide on how to playaround with it.
Real Use Case
Imagine that you have a StudentRepository
which return list of Students from your database. It probably have a predicate/filter (based on use case) where probably return no data.
public class StudentRepo
{
public async Task<IEnumerable<Student>> GetStudents()
{
// code logic
var result = await context.Students.TolistAsync(); //call to db
if (result.Count == 0)
return new List<Student>(); // here is the problem ⚠️
return result;
}
}
Lets Benchmark
This code defines a benchmarking class BenchmarkEmptyCollection.cs
using the BenchmarkDotNet library to compare the performance of two methods for returning an empty collection of Student.cs
objects.
- ReturnNew(): Returns a void new empty
List<Student>
. - ReturnEmpty(): Returns avoid sequence using
Enumerable.Empty<Student>()
- The
[MemoryDiagnoser]
attribute is used to analyze memory usage, and the[Benchmark(Baseline = true)]
[MemoryDiagnoser]
public class BenchmarkEmptyCollection
{
private IEnumerable<Student> _students;
[Benchmark(Baseline = true)]
public void ReturnNew()
{
_students = new List<Student>();
}
[Benchmark]
public void ReturnEmtpy()
{
_students = Enumerable.Empty<Student>();
}
}
Note : Make sure run in
release
mode
Result
The results really shock me since the difference is significantly big. ReturnEmtpy()
is significantly faster and more memory-efficient than ReturnNew()
. It takes about 31%
of the time of ReturnNew()
and performs no allocations 🤩, whereas ReturnNew()
allocates 32 bytes
and involves some garbage collection which is bad in performance matters. This demonstrates that Enumerable.Empty<Student>()
is more efficient for returning an empty sequence compared to creating a new List<Student>
.
| Method | Mean | Error | StdDev | Ratio | RatioSD | Gen0 | Allocated | Alloc Ratio |
|------------ |---------:|----------:|----------:|------:|--------:|-------:|----------:|------------:|
| ReturnNew | 6.693 ns | 0.1515 ns | 0.2268 ns | 1.00 | 0.05 | 0.0153 | 32 B | 1.00 |
| ReturnEmtpy | 2.074 ns | 0.0611 ns | 0.0572 ns | 0.31 | 0.01 | - | - | 0.00 |
Only Enumerable can used this Empty? How about Array?
Absolutely guys ❤️. Array type can use as below :
// code ..
return Array.Empty<T>(); // change <T> to your type
// code ..
Summary
Our example prove that by tweaking few code can have a significant boost in our code. Moreover, this shows that instead of creating a new empty collection (like an empty array or list), Enumerable.Empty<T>()
provides a more efficient way to represent an empty sequence. It avoids unnecessary allocations and provides a shared, static instance of an empty sequence, which can improve performance and reduce memory usage.