@@ -13,11 +13,11 @@ Inspired by [John Thiriet](https://github.com/johnthiriet)'s blog posts: [Removi
1313Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/
1414
1515- ` SafeFireAndForget `
16- - An extension method to safely fire-and-forget a ` Task `
16+ - An extension method to safely fire-and-forget a ` Task ` or a ` ValueTask `
1717 - Ensures the ` Task ` will rethrow an ` Exception ` if an ` Exception ` is caught in ` IAsyncStateMachine.MoveNext() `
1818- ` WeakEventManager `
1919 - Avoids memory leaks when events are not unsubscribed
20- - Used by ` AsyncCommand ` and ` AsyncCommand<T> `
20+ - Used by ` AsyncCommand ` , ` AsyncCommand<T> ` , ` AsyncValueCommand ` , ` AsyncValueCommand <T>`
2121- [ Usage instructions] ( #asyncawaitbestpractices-3 )
2222
2323### AsyncAwaitBestPractices.MVVM
@@ -31,6 +31,12 @@ Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/
3131 - ` AsyncCommand : IAsyncCommand `
3232 - ` IAsyncCommand<T> : ICommand `
3333 - ` AsyncCommand<T> : IAsyncCommand<T> `
34+
35+ - Allows for ` ValueTask ` to safely be used asynchronously with ` ICommand ` :
36+ - ` IAsyncValueCommand : ICommand `
37+ - ` AsyncValueCommand : IAsyncValueCommand `
38+ - ` IAsyncValueCommand<T> : ICommand `
39+ - ` AsyncValueCommand<T> : IAsyncValueCommand<T> `
3440- [ Usage instructions] ( #asyncawaitbestpracticesmvvm-2 )
3541
3642## Setup
@@ -129,7 +135,11 @@ An extension method to safely fire-and-forget a `Task`.
129135public static async void SafeFireAndForget (this System .Threading .Tasks .Task task , bool continueOnCapturedContext = false , System .Action < System .Exception > onException = null )
130136```
131137
132- #### Basic Usage
138+ ``` csharp
139+ public static async void SafeFireAndForget (this System .Threading .Tasks .ValueTask task , bool continueOnCapturedContext = false , System .Action < System .Exception > onException = null )
140+ ```
141+
142+ #### Basic Usage - Task
133143
134144``` csharp
135145void HandleButtonTapped (object sender , EventArgs e )
@@ -148,6 +158,30 @@ async Task ExampleAsyncMethod()
148158}
149159```
150160
161+ #### Basic Usage - ValueTask
162+
163+ If you're new to ValueTask, check out this great write-up, [ Understanding the Whys, Whats, and Whens of ValueTask
164+ ] ( https://blogs.msdn.microsoft.com/dotnet/2018/11/07/understanding-the-whys-whats-and-whens-of-valuetask?WT.mc_id=asyncawaitbestpractices-github-bramin ) .
165+
166+ ``` csharp
167+ void HandleButtonTapped (object sender , EventArgs e )
168+ {
169+ // Allows the async ValueTask method to safely run on a different thread while the calling thread continues, not awaiting its completion
170+ // onException: If an Exception is thrown, print it to the Console
171+ ExampleValueTaskMethod ().SafeFireAndForget (onException : ex => Console .WriteLine (ex ));
172+
173+ // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread
174+ // ...
175+ }
176+
177+ async ValueTask ExampleValueTaskMethod ()
178+ {
179+ var random = new Random ();
180+ if (random .Next (10 ) > 9 )
181+ await Task .Delay (1000 );
182+ }
183+ ```
184+
151185#### Advanced Usage
152186
153187``` csharp
@@ -175,17 +209,32 @@ void HandleButtonTapped(object sender, EventArgs e)
175209 ExampleAsyncMethod ().SafeFireAndForget <WebException >(onException : ex =>
176210 {
177211 if (ex .Response is HttpWebResponse webResponse )
178- Console .WriteLine ($" Status Code: {webResponse .StatusCode }" );
212+ Console .WriteLine ($" Task Exception\n Status Code: {webResponse .StatusCode }" );
213+ });
214+
215+ ExampleValueTaskMethod ().SafeFireAndForget <WebException >(onException : ex =>
216+ {
217+ if (ex .Response is HttpWebResponse webResponse )
218+ Console .WriteLine ($" ValueTask Error\n Status Code: {webResponse .StatusCode }" );
179219 });
180220
181- // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread
221+ // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` and `ExampleValueTaskMethod()` run in the background
182222 }
183223
184224async Task ExampleAsyncMethod ()
185225{
186226 await Task .Delay (1000 );
187227 throw new WebException ();
188228}
229+
230+ async ValueTask ExampleValueTaskMethod ()
231+ {
232+ var random = new Random ();
233+ if (random .Next (10 ) > 9 )
234+ await Task .Delay (1000 );
235+
236+ throw new WebException ();
237+ }
189238```
190239
191240### ` WeakEventManager `
@@ -282,15 +331,15 @@ Allows for `Task` to safely be used asynchronously with `ICommand`:
282331
283332``` csharp
284333public AsyncCommand (Func < T , Task > execute ,
285- Func < object , bool > canExecute = null ,
286- Action < Exception > onException = null ,
334+ Func < object , bool > ? canExecute = null ,
335+ Action < Exception > ? onException = null ,
287336 bool continueOnCapturedContext = false )
288337```
289338
290339``` csharp
291340public AsyncCommand (Func < Task > execute ,
292- Func < object , bool > canExecute = null ,
293- Action < Exception > onException = null ,
341+ Func < object , bool > ? canExecute = null ,
342+ Action < Exception > ? onException = null ,
294343 bool continueOnCapturedContext = false )
295344```
296345
@@ -365,6 +414,110 @@ public class ExampleClass
365414}
366415```
367416
417+ ### ` AsyncValueCommand `
418+
419+ Allows for ` ValueTask ` to safely be used asynchronously with ` ICommand ` .
420+
421+ If you're new to ValueTask, check out this great write-up, [ Understanding the Whys, Whats, and Whens of ValueTask
422+ ] ( https://blogs.msdn.microsoft.com/dotnet/2018/11/07/understanding-the-whys-whats-and-whens-of-valuetask?WT.mc_id=asyncawaitbestpractices-github-bramin ) .
423+
424+ - ` AsyncValueCommand<T> : IAsyncValueCommand<T> `
425+ - ` IAsyncValueCommand<T> : ICommand `
426+ - ` AsyncValueCommand : IAsyncValueCommand `
427+ - ` IAsyncValueCommand : ICommand `
428+
429+ ``` csharp
430+ public AsyncValueCommand (Func < T , ValueTask > execute ,
431+ Func < object , bool > ? canExecute = null ,
432+ Action < Exception > ? onException = null ,
433+ bool continueOnCapturedContext = false )
434+ ```
435+
436+ ``` csharp
437+ public AsyncValueCommand (Func < ValueTask > execute ,
438+ Func < object , bool > ? canExecute = null ,
439+ Action < Exception > ? onException = null ,
440+ bool continueOnCapturedContext = false )
441+ ```
442+
443+ ``` csharp
444+ public class ExampleClass
445+ {
446+ bool _isBusy ;
447+
448+ public ExampleClass ()
449+ {
450+ ExampleValueTaskCommand = new AsyncValueCommand (ExampleValueTaskMethod );
451+ ExampleValueTaskIntCommand = new AsyncValueCommand <int >(ExampleValueTaskMethodWithIntParameter );
452+ ExampleValueTaskExceptionCommand = new AsyncValueCommand (ExampleValueTaskMethodWithException , onException : ex => Debug .WriteLine (ex .ToString ()));
453+ ExampleValueTaskCommandWithCanExecuteChanged = new AsyncValueCommand (ExampleValueTaskMethod , _ => ! IsBusy );
454+ ExampleValueTaskCommandReturningToTheCallingThread = new AsyncValueCommand (ExampleValueTaskMethod , continueOnCapturedContext : true );
455+ }
456+
457+ public IAsyncValueCommand ExampleValueTaskCommand { get ; }
458+ public IAsyncValueCommand <int > ExampleValueTaskIntCommand { get ; }
459+ public IAsyncValueCommand ExampleValueTaskExceptionCommand { get ; }
460+ public IAsyncValueCommand ExampleValueTaskCommandWithCanExecuteChanged { get ; }
461+ public IAsyncValueCommand ExampleValueTaskCommandReturningToTheCallingThread { get ; }
462+
463+ public bool IsBusy
464+ {
465+ get => _isBusy ;
466+ set
467+ {
468+ if (_isBusy != value )
469+ {
470+ _isBusy = value ;
471+ ExampleValueTaskCommandWithCanExecuteChanged .RaiseCanExecuteChanged ();
472+ }
473+ }
474+ }
475+
476+ async ValueTask ExampleValueTaskMethod ()
477+ {
478+ var random = new Random ();
479+ if (random .Next (10 ) > 9 )
480+ await Task .Delay (1000 );
481+ }
482+
483+ async ValueTask ExampleValueTaskMethodWithIntParameter (int parameter )
484+ {
485+ var random = new Random ();
486+ if (random .Next (10 ) > 9 )
487+ await Task .Delay (parameter );
488+ }
489+
490+ async ValueTask ExampleValueTaskMethodWithException ()
491+ {
492+ var random = new Random ();
493+ if (random .Next (10 ) > 9 )
494+ await Task .Delay (1000 );
495+
496+ throw new Exception ();
497+ }
498+
499+ void ExecuteCommands ()
500+ {
501+ _isBusy = true ;
502+
503+ try
504+ {
505+ ExampleValueTaskCommand .Execute (null );
506+ ExampleValueTaskIntCommand .Execute (1000 );
507+ ExampleValueTaskExceptionCommand .Execute (null );
508+ ExampleValueTaskCommandReturningToTheCallingThread .Execute (null );
509+
510+ if (ExampleValueTaskCommandWithCanExecuteChanged .CanExecute (null ))
511+ ExampleValueTaskCommandWithCanExecuteChanged .Execute (null );
512+ }
513+ finally
514+ {
515+ _isBusy = false ;
516+ }
517+ }
518+ }
519+ ```
520+
368521## Learn More
369522- [ Removing Async Void] ( https://johnthiriet.com/removing-async-void/ )
370523- [ MVVM Going Async with Async Command] ( https://johnthiriet.com/mvvm-going-async-with-async-command/ )
0 commit comments