Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create empty IAsyncEnumerable

I have an interface which is written like this:

public interface IItemRetriever
{
    public IAsyncEnumerable<string> GetItemsAsync();
}

I want to write an empty implementation that returns no item, like so:

public class EmptyItemRetriever : IItemRetriever
{
    public IAsyncEnumerable<string> GetItemsAsync()
    {
       // What do I put here if nothing is to be done?
    }
}

If it was a plain IEnumerable, I would return Enumerable.Empty<string>();, but I didn't find any AsyncEnumerable.Empty<string>().

Workarounds

I found this which works but is quite weird:

public async IAsyncEnumerable<string> GetItemsAsync()
{
    await Task.CompletedTask;
    yield break;
}

Any idea?

like image 220
cube45 Avatar asked Dec 22 '19 10:12

cube45


3 Answers

If you install the System.Linq.Async package, you should be able to use AsyncEnumable.Empty<string>(). Here's a complete example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        IAsyncEnumerable<string> empty = AsyncEnumerable.Empty<string>();
        var count = await empty.CountAsync();
        Console.WriteLine(count); // Prints 0
    }
}
like image 159
Jon Skeet Avatar answered Nov 06 '22 09:11

Jon Skeet


If for any reason you don't want to install the package which is mentioned in Jon's answer, you can create the method AsyncEnumerable.Empty<T>() like this:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public static class AsyncEnumerable
{
    public static IAsyncEnumerator<T> Empty<T>() => EmptyAsyncEnumerator<T>.Instance;

    class EmptyAsyncEnumerator<T> : IAsyncEnumerator<T>
    {
        public static readonly EmptyAsyncEnumerator<T> Instance = 
            new EmptyAsyncEnumerator<T>();
        public T Current => default!;
        public ValueTask DisposeAsync() => default;
        public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(false);
    }
}

Note: The answer doesn't discourage using the System.Linq.Async package. This answer provides a brief implementation of AsyncEnumerable.Empty<T>() for cases that you need it and you cannot/don't want to use the package. You can find the implementation used in the package here.

like image 21
Reza Aghaei Avatar answered Nov 06 '22 08:11

Reza Aghaei


I wanted to avoid installing System.Linq.Async (due to its issues with namespace collisions) but the previous answer does not actually implement IAsyncEnumerable<T> as requested in the original question. Here's a full solution which implements that interface to easily allow calling AsyncEnumerable.Empty<T> in the same way that Enumerable.Empty<T> works today.

public static class AsyncEnumerable
{
    /// <summary>
    /// Creates an <see cref="IAsyncEnumerable{T}"/> which yields no results, similar to <see cref="Enumerable.Empty{TResult}"/>.
    /// </summary>
    public static IAsyncEnumerable<T> Empty<T>() => EmptyAsyncEnumerator<T>.Instance;

    private class EmptyAsyncEnumerator<T> : IAsyncEnumerator<T>, IAsyncEnumerable<T>
    {
        public static readonly EmptyAsyncEnumerator<T> Instance = new EmptyAsyncEnumerator<T>();
        public T Current => default;
        public ValueTask DisposeAsync() => default;
        public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
        {
            cancellationToken.ThrowIfCancellationRequested();
            return this;
        }
        public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(false);
    }
}
like image 7
Tobias J Avatar answered Nov 06 '22 09:11

Tobias J