- VB.NET (even as-of August 2023) does not support
IAsyncDisposable
in Using
.
- Nor does it support using
Await
inside Finally
.
- So the best you can do to try some ugly hack and/or port-over some old C# tricks...
Approach 1: Use C# for the using
part:
Just create a new empty C# Class Library project with a single helper method which you can call from VB.NET, something like this:
(Just don't expect to be able to use ConfigureAwait(False)
- even in C# it's hard).
public static class ArghExtensions
{
public static async Task UsingAsyncDisposableAsync( this IAsyncDisposable subject, CancellationToken cancellationToken, Func<CancellationToken,Task> asyncBody ) // Need this parameter order for ergonomic reasons.
{
await using( subject )
{
// Argument validation (preconditions) needs to be done from within the implicit try/finally, otherwise `subject` won't be disposed if `asyncBody` is null.
if( subject is null ) throw new ArgumentNullException(nameof(subject));
if( asyncBody is null ) throw new ArgumentNullException(nameof(asyncBody));
await asyncBody( cancellationToken ).ConfigureAwait(false);
}
}
}
Then call it from VB.NET:
Imports ArghExtensions
Async Function FoobarAsync( cancellationToken As CancellationToken ) As Task
Dim d AS IAsyncDisposable = GetSomethingDisposable()
Await d.UsingAsyncDisposableAsync( Async Function( ct As CancellationToken )
' do async stuff in here
End Function )
End Function
An alternative way of implementing this method, without using any C# step, is to use Reflection.Emit from within VB.NET to generate equivalent (and safe) IL
that reimplements that UsingAsyncDisposableAsync
method - though this will be a lot of work for probably little gain tbh.
Approach 2: Embrace VB.NET's verbosity
Instead of using any C# code, we can actually still do it all in VB.NET, with the caveat that you need to use Try/Catch
and not Try/Finally
as VB.NET still can't use Await
inside Finally
:
Async Function FoobarAsync( cancellationToken As CancellationToken ) As Task
' See https://stackoverflow.com/a/16626313/159145
Dim capturedException As ExceptionDispatchInfo = Nothing
Dim d AS IAsyncDisposable = GetSomethingDisposable()
Try
' do async stuff in here, before the DisposeAsync call below.
Await d.DisposeAsync().ConfigureAwait(False)
d = Nothing
Catch x As Exception
capturedException = ExceptionDispatchInfo.Capture( x )
End Try
If capturedException IsNot Nothing Then
If d IsNot Nothing Then
Await d.DisposeAsync().ConfigureAwait(False)
d = Nothing
End If
capturedException.Throw()
End If
End Function
Function GetSomethingDisposable() As IAsyncDisposable
Return New MemoryStream()
End Function
I haven't run this code through any static-analysis, but I'm confident this won't trigger CA2000