A better approach to "await using" and IAsyncDisposable with ConfigureAwait #8778
Unanswered
RichardR-cg
asked this question in
Language Ideas
Replies: 1 comment
-
Related: #2661 |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Surely there has to be a better way of making "await using", IAsyncDisposable and ConfigureAwait play nicely. Maybe I've missed a simple solution to this, but if I have, it certainly isn't obvious, and I'd welcome the enlightenment.
Failing that, I'm hoping that the clever people on the C# language team can provide some nicer "syntactic sugar" to wrap that up more neatly in a future version of C#. I don't have a proposed solution, but the situation is described below.
I love the IDisposable interface and the "using" block. It limits the scope of things, and ensures that resources being used are disposed.
I also like that if you're using analyzers, it can prompt you to use "using" when you might not otherwise notice that the thing you're using is IDisposable via CA2000: Dispose objects before losing scope (code analysis) - .NET | Microsoft Learn.
I'm not a huge fan of the change to allow "using" statements instead of blocks, as it relaxes the scope (sometimes with unintended consequences due to the extending of the scope of that variable, and therefore changing the point at which it is disposed); which becomes relevant later.
Along came
async
, and in C#8,IAsyncDisposable
was introduced (Implement a DisposeAsync method | Microsoft Learn), and although the second paragraph starts by saying......that certainly isn't always the case (I was recently using a Microsoft class which implements IAsyncDisposable but not IDisposable).
IAsyncDispoable means that we can do
await using
... except that the correct way of writing that (shown in the link above) isn't what you'd expect. The IAsyncDisposable article linked above gives an example of how to write it:There is a drive to use ConfigureAwait (for reasons given in CA2007: Do not directly await a Task (code analysis) - .NET | Microsoft Learn), which changes the return type.
Notice that this means that the variable which is IAsyncDisposable is no longer constrained by the scope of the using block. So you could write code which uses that variable after the using block, the code probably wouldn't work; but it would compile. The solution to that would be that you have to wrap the above code in braces (just braces, to group the lines shown above together, no special keywords required). Yeuch, and an extra level of indent, and probably some analyzers telling you to remove the "redundant" braces.
Interestingly, unlike CA2000 for IDisposable the analyzers do not currently point out if you have something which is IAsyncDisposable, and if you aren't doing an "await using" on it (or its ConfigureAwait), so you need to keep an eye out for this yourself. Perhaps it was the added complexity of the ConfigureAwait syntax which made it hard for them to get the Analyzer to detect (I'm sure it's possible, but it won't be trivial). The 4 examples given in https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/implementing-disposeasync#stacked-usings (3 acceptable, and one unacceptable) certainly show that it would be tricky to write that analyzer properly.
So it almost seems like the best way to write this is:
...since this at least doesn't require an extra level of indentation; it's just weird that the thing you're doing the using for is just part of the braced code, rather than it being explicitly clear that that's why the braces are there (like you'd have with a "using block").
And finally... to cap it all, it would appear that if you use a class which implements both IDisposable and IAsyncDisposable, then it isn't possible to write code which isn't complained about by either CA2000 or CA2007: CA2000 emitted when await using (false positive) · Issue #3042 · dotnet/roslyn-analyzers (github.com). I don't have a solution for that, I'm just suppressing the one that I think is least bad to ignore (CA2007).
Beta Was this translation helpful? Give feedback.
All reactions