
# C# LDM notes for Dec 12, 2018

## Agenda

1. Open issues with async streams
2. Pattern dispose

## Discussion

### Cancellation in GetAsyncEnumerator

We previously decided that GetAsyncEnumerator takes a CancellationToken.
When do we check if it's been cancelled?

Our design for cancellation in the user code itself is to have the user
write the core logic for their IAsyncEnumerable using an IAsyncEnumerator
iterator method. The user can manually check for cancellation inside the
iterator body.

```C#
class C : IAsyncEnumerable
{
    IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken token)
    {
        ...
        token.ThrowIfCancelled();
        ...
    }
}
```

*Q: Do we want to generate any checks manually? One option is to check the
cancellation token on every MoveNext call.*

This comes down to: where is it most useful to request cancellation?

The most important place to include the cancellation token is in the await
calls themselves. This isn't compiler generated code at all -- if the
expressions being awaited need a CancellationToken the user will need to
pass it themselves. Other places, like every entry to MoveNextAsync, will
be called regularly, but will only be able to cancel synchronous code,
instead of the long-running async operation.

**Conclusion**

The compiler will not generate checks for cancellation for now. If we
get user feedback that it's important, we will revisit this decision.

### Pattern binding

Which pattern do we want the compiler to recognize for GetAsyncEnumerator?

Binding options for some expression target of a `foreach`, `e`:

1. `e.GetAsyncEnumerable()`
2. `e.GetAsyncEnumerable(default(CancellationToken))`

One of the primary drawbacks for (1) is that all users must make their
CancellationToken parameters optional. This may be a problem for implementors
who consider it very important to have a CancellationToken.

**Conclusion**

We will attempt to bind the call
`e.GetAsyncEnumerable(default(CancellationToken))` and if it succeeds and has
a conforming return type, the pattern binds successfully.

### Recognizing an iterator

Consider the following:

```C#
class Program
{
    static async IAsyncEnumerable<string> M()
    {
        throw new NotImplementedException();
    }
}
```

As currently designed, this is illegal. `async` methods must either return
a Task-like type or return `IAsyncEnumerable/IAsyncEnumerator` and contain
a yield statement. This is neither.

The proposal is to produce an error. There are a number of fixes:

1. Add a `yield` in the body.
2. Remove the `async`.

**Conclusion**

Add an error, undecided on the error message.

### When we start recognizing pattern Dispose, do we call it in foreach?

Certainly, it seems very important for the feature. Since a ref struct
cannot implement interfaces, pattern Dispose is the only way to implement
disposal.

There are a number of possible breaking changes:

1. A pattern enumerable type did not implement IDisposable, but had a
   Dispose method. This method would now be call.
2. If there are two Dispose extension methods, that will now produce
   an ambiguity and thus a compilation error.

**Conclusion**

Let's narrow the pattern-based Dispose change down to ref structs only.
This means every other type will be required to implement IDisposable.