For a while I've been trying to get my head around the whole async/await model that C# uses for asynchronous code. The addition of async streams (the IAsyncEnumerable<T> type) seemed really cool, especially for some code that I was writing.
Best practice when creating an async method is to include a CancellationToken parameter and use it for cancelling your async processes. (Ideally by passing it to the underlying async method calls used in your method.)
When creating a method that returns an async stream (an IAsyncEnumerable<T>) the documentation states that your CancellationToken parameter should be decorated with the [EnumeratorCancellation] attribute and then the token passed using the .WithCancellation() method on the IAsyncEnumerable<T> itself.
However, I must be doing something wrong because this still triggers warning:
CA2016: Forward the CancellationToken parameter to methods that take one
This warning appears regardless of if I do it the more standard way:
async IAsyncEnumerable<aThingo> GetFlibbityStream([EnumeratorCancellation] CancellationToken cancellationToken = default) {
    aThingo slowValue = null;
    do {
        aThingo slowValue = await GetThatThingo(cancellationToken);
        yield return slowValue;
    while (slowValue != null);
}
async Task DoingStuff(CancellationToken cancellationToken) {
    await foreach(var thng in ThingStreamCreator.GetFlibbityStream().WithCancellation(cancellationToken)) {
        CrushThatThing(thng);
    }
}
Or at points where I need to get the AsyncEnumerator itself (because I need to iterate through two async streams together, but not necessarily at the same rate.)
async Task ValidatingThingsAsync(CancellationToken cancellationToken) {
    await using IAsyncEnumerator<aThingo> srcEnumerator = source.ThingValidityAsyncStream(dateCutOff).GetAsyncEnumerator(cancellationToken);
    ... streamy stuff ....
}
All of my async stream methods have a default value for the CancellationToken which makes them optional parameters. I think perhaps part of my problem is the WithCancellation() method is intended for use cases where you already have the IAsyncStream<T> but didn't necessarily pass the cancellation token to it. But that doesn't entirely make sense and it feels like I am either passing in the cancellation token too often or not enough (or doing the wrong one of those when I should be doing the other.)
Am I simply misusing WithCancellation() and GetAsyncEnumerator() by unnecessarily passing the cancellation token when I should simply be passing it to the async stream method directly in these cases?
Basically I should not be using WithCancellation() and I shouldn't be passing anything to GetAsyncEnumerator() and should instead remove the default value for the CancellationToken on my async stream methods and pass the token directly to them. Basically I think I'm confused by the number of different ways to pass a CancellationToken to an async stream and identifying which is the correct method to use at the time...
 
                        
According to the specification:
You're calling
GetFlibbityStreammethod, so this is the case #1. You should passCancellationTokendirectly to the method and should not chainGetFlibbityStreamwithWithCancellation. Otherwise rule analyzer for CA2016 will emit warning, and it will be right.WithCancellationis intended for the case #2. For example, there is some library type with property or method, which returnsIAsyncEnumerable<T>and does not allow to passCancellationTokendirectly.Like this one:
This still supports cancellation, but the only way to pass token to
IFlibbityService.FlibbityStreamis to useWithCancellation:Back to your code, just throw away
WithCancellationand pass token directly: