Skip to content

async foreach binds to IAsyncDisposable.DisposeAsync() rather than pattern based DisposeAsync() #59955

@cston

Description

@cston

The following prints IAsyncDisposable.DisposeAsync():

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

await foreach (var i in new AsyncEnumerable())
{
}

struct AsyncEnumerable : IAsyncEnumerable<int>
{
    public AsyncEnumerator GetAsyncEnumerator(CancellationToken token) => new AsyncEnumerator();
    IAsyncEnumerator<int> IAsyncEnumerable<int>.GetAsyncEnumerator(CancellationToken token) => new AsyncEnumerator();
}

struct AsyncEnumerator : IAsyncEnumerator<int>
{
    public int Current => 0;
    public async ValueTask<bool> MoveNextAsync() => false;
    public async ValueTask DisposeAsync() { Console.WriteLine("DisposeAsync()"); }
    async ValueTask IAsyncDisposable.DisposeAsync() { Console.WriteLine("IAsyncDisposable.DisposeAsync()"); }
}

See sharplab.io.

From async-streams spec:

The compiler will bind to the pattern-based APIs if they exist, preferring those over using the interface (the pattern may be satisfied with instance methods or extension methods).

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions