diff --git a/README.md b/README.md index bd343ac..efdb431 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,16 @@ Extensions for `System.Threading.Tasks.Task`, inspired by [John Thiriet](https:/ An extension method to safely fire-and-forget a `Task`: - `SafeFireAndForget` +```csharp +public static async void SafeFireAndForget(this System.Threading.Tasks.Task task, bool continueOnCapturedContext = true, System.Action onException = null) +``` + ```csharp void HandleButtonTapped(object sender, EventArgs e) { // Allows the async Task method to safely run on a different thread while not awaiting its completion - ExampleAsyncMethod().SafeFireAndForget(); + // If an exception is thrown, Console.WriteLine + ExampleAsyncMethod().SafeFireAndForget(onException: ex => Console.WriteLine(ex.Message)); // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread // ... @@ -62,6 +67,22 @@ Allows for `Task` to safely be used asynchronously with `ICommand`: - `AsyncCommand : IAsyncCommand` - `IAsyncCommand : ICommand` + +```csharp +public AsyncCommand(Func execute, + Func canExecute = null, + Action onException = null, + bool continueOnCapturedContext = true) +``` + +```csharp +public AsyncCommand(Func execute, + Func canExecute = null, + Action onException = null, + bool continueOnCapturedContext = true) +``` + + ```csharp public class ExampleClass { diff --git a/Src/AsyncAwaitBestPractices.MVVM.nuspec b/Src/AsyncAwaitBestPractices.MVVM.nuspec index 7fa5249..0e12711 100644 --- a/Src/AsyncAwaitBestPractices.MVVM.nuspec +++ b/Src/AsyncAwaitBestPractices.MVVM.nuspec @@ -2,7 +2,7 @@ AsyncAwaitBestPractices.MVVM - 0.9.0 + 1.0.0 Task Extensions for MVVM Brandon Minnick, John Thiriet Brandon Minnick diff --git a/Src/AsyncAwaitBestPractices.MVVM/AsyncCommand.cs b/Src/AsyncAwaitBestPractices.MVVM/AsyncCommand.cs index 7f7b681..9a7cf75 100644 --- a/Src/AsyncAwaitBestPractices.MVVM/AsyncCommand.cs +++ b/Src/AsyncAwaitBestPractices.MVVM/AsyncCommand.cs @@ -21,13 +21,13 @@ public sealed class AsyncCommand : IAsyncCommand /// Initializes a new instance of the class. /// /// The Function executed when Execute or ExecuteAysnc is called. This does not check canExecute before executing and will execute even if canExecute is false - /// If set to true continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to false continue on a different context; this will allow the Synchronization Context to continue on a different thread - /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown /// The Function that verifies whether or not AsyncCommand should execute. + /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown + /// If set to true continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to false continue on a different context; this will allow the Synchronization Context to continue on a different thread public AsyncCommand(Func execute, - bool continueOnCapturedContext = true, + Func canExecute = null, Action onException = null, - Func canExecute = null) + bool continueOnCapturedContext = true) { _execute = execute; _continueOnCapturedContext = continueOnCapturedContext; @@ -71,35 +71,22 @@ public AsyncCommand(Func execute, /// Data used by the command. If the command does not require data to be passed, this object can be set to null. public Task ExecuteAsync(object parameter) { - switch (parameter) - { - case T validParameter: - return ExecuteAsync(validParameter); - - case null when !typeof(T).IsValueType: - - return ExecuteAsync((T)parameter); + if (parameter is T validParameter) + return ExecuteAsync(validParameter); + else if (parameter is null && !typeof(T).IsValueType) + return ExecuteAsync((T)parameter); - default: - throw new InvalidCommandParameterException(typeof(T), parameter.GetType()); - } + throw new InvalidCommandParameterException(typeof(T), parameter.GetType()); } void ICommand.Execute(object parameter) { - switch (parameter) - { - case T validParameter: - _execute?.Invoke(validParameter)?.SafeFireAndForget(_continueOnCapturedContext, _onException); - break; - - case null when !typeof(T).IsValueType: - _execute?.Invoke((T)parameter)?.SafeFireAndForget(_continueOnCapturedContext, _onException); - break; - - default: - throw new InvalidCommandParameterException(typeof(T), parameter.GetType()); - } + if (parameter is T validParameter) + _execute?.Invoke(validParameter)?.SafeFireAndForget(_continueOnCapturedContext, _onException); + else if (parameter is null && !typeof(T).IsValueType) + _execute?.Invoke((T)parameter)?.SafeFireAndForget(_continueOnCapturedContext, _onException); + else + throw new InvalidCommandParameterException(typeof(T), parameter.GetType()); } #endregion } @@ -125,9 +112,9 @@ public sealed class AsyncCommand : IAsyncCommand /// If an exception is thrown in the Task, onException will execute. If onException is null, the exception will be re-thrown /// The Function that verifies whether or not AsyncCommand should execute. public AsyncCommand(Func execute, - bool continueOnCapturedContext = true, + Func canExecute = null, Action onException = null, - Func canExecute = null) + bool continueOnCapturedContext = true) { _execute = execute; _continueOnCapturedContext = continueOnCapturedContext; diff --git a/Src/AsyncAwaitBestPractices.MVVM/InvalidCommandParameterException.cs b/Src/AsyncAwaitBestPractices.MVVM/InvalidCommandParameterException.cs index 3c635bc..14248ce 100644 --- a/Src/AsyncAwaitBestPractices.MVVM/InvalidCommandParameterException.cs +++ b/Src/AsyncAwaitBestPractices.MVVM/InvalidCommandParameterException.cs @@ -29,7 +29,7 @@ public InvalidCommandParameterException(string message) : base(message) /// /// Initializes a new instance of the class. /// - public InvalidCommandParameterException() : base() + public InvalidCommandParameterException() { } diff --git a/Src/AsyncAwaitBestPractices.UnitTests/Tests_AsyncCommand.cs b/Src/AsyncAwaitBestPractices.UnitTests/Tests_AsyncCommand.cs index 68827a8..28c1c77 100644 --- a/Src/AsyncAwaitBestPractices.UnitTests/Tests_AsyncCommand.cs +++ b/Src/AsyncAwaitBestPractices.UnitTests/Tests_AsyncCommand.cs @@ -90,7 +90,7 @@ public async Task AsyncCommand_ExecuteAsync_InvalidReferenceTypeParameter_Test() public async Task AsyncCommand_ExecuteAsync_NoParameter_ContinueOnCapturedContext_Test(bool continueOnCapturedContext) { //Arrange - AsyncCommand command = new AsyncCommand(NoParameterTask, continueOnCapturedContext); + AsyncCommand command = new AsyncCommand(NoParameterTask, continueOnCapturedContext: continueOnCapturedContext); var callingThreadId = Thread.CurrentThread.ManagedThreadId; //Act @@ -107,7 +107,7 @@ public async Task AsyncCommand_ExecuteAsync_NoParameter_ContinueOnCapturedContex public async Task AsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext_Test(int parameter, bool continueOnCapturedContext) { //Arrange - AsyncCommand command = new AsyncCommand(IntParameterTask, continueOnCapturedContext); + AsyncCommand command = new AsyncCommand(IntParameterTask, continueOnCapturedContext: continueOnCapturedContext); var callingThreadId = Thread.CurrentThread.ManagedThreadId; //Act @@ -123,7 +123,7 @@ public async Task AsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext_ public void AsyncCommand_Parameter_CanExecuteTrue_Test() { //Arrange - AsyncCommand command = new AsyncCommand(IntParameterTask, canExecute: CanExecuteTrue); + AsyncCommand command = new AsyncCommand(IntParameterTask, CanExecuteTrue); //Act diff --git a/Src/AsyncAwaitBestPractices.UnitTests/Tests_IAsyncCommand.cs b/Src/AsyncAwaitBestPractices.UnitTests/Tests_IAsyncCommand.cs index 55179c3..33227b6 100644 --- a/Src/AsyncAwaitBestPractices.UnitTests/Tests_IAsyncCommand.cs +++ b/Src/AsyncAwaitBestPractices.UnitTests/Tests_IAsyncCommand.cs @@ -90,7 +90,7 @@ public async Task AsyncCommand_ExecuteAsync_InvalidReferenceTypeParameter_Test() public async Task IAsyncCommand_ExecuteAsync_NoParameter_ContinueOnCapturedContext_Test(bool continueOnCapturedContext) { //Arrange - IAsyncCommand command = new AsyncCommand(NoParameterTask, continueOnCapturedContext); + IAsyncCommand command = new AsyncCommand(NoParameterTask, continueOnCapturedContext: continueOnCapturedContext); var callingThreadId = Thread.CurrentThread.ManagedThreadId; //Act @@ -107,7 +107,7 @@ public async Task IAsyncCommand_ExecuteAsync_NoParameter_ContinueOnCapturedConte public async Task IAsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext_Test(object parameter, bool continueOnCapturedContext) { //Arrange - IAsyncCommand command = new AsyncCommand(IntParameterTask, continueOnCapturedContext); + IAsyncCommand command = new AsyncCommand(IntParameterTask, continueOnCapturedContext: continueOnCapturedContext); var callingThreadId = Thread.CurrentThread.ManagedThreadId; //Act @@ -123,7 +123,7 @@ public async Task IAsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext public void IAsyncCommand_Parameter_CanExecuteTrue_Test() { //Arrange - IAsyncCommand command = new AsyncCommand(IntParameterTask, canExecute: CanExecuteTrue); + IAsyncCommand command = new AsyncCommand(IntParameterTask, CanExecuteTrue); //Act diff --git a/Src/AsyncAwaitBestPractices.nuspec b/Src/AsyncAwaitBestPractices.nuspec index d5cd76a..7de2d21 100644 --- a/Src/AsyncAwaitBestPractices.nuspec +++ b/Src/AsyncAwaitBestPractices.nuspec @@ -2,7 +2,7 @@ AsyncAwaitBestPractices - 0.9.0 + 1.0.0 Task Extensions for System.Threading.Tasks Brandon Minnick, John Thiriet Brandon Minnick diff --git a/Src/AsyncAwaitBestPractices/TaskExtensions.cs b/Src/AsyncAwaitBestPractices/TaskExtensions.cs index e9d7fc0..dfef8c8 100644 --- a/Src/AsyncAwaitBestPractices/TaskExtensions.cs +++ b/Src/AsyncAwaitBestPractices/TaskExtensions.cs @@ -21,15 +21,10 @@ public static async void SafeFireAndForget(this System.Threading.Tasks.Task task } catch (System.Exception ex) { - switch (onException) - { - case null: - throw; + if (onException is null) + throw; - default: - onException?.Invoke(ex); - break; - } + onException?.Invoke(ex); } } } diff --git a/Src/HackNews.Droid/HackerNews.Droid.csproj b/Src/HackNews.Droid/HackerNews.Droid.csproj index 176a617..75ecb02 100644 --- a/Src/HackNews.Droid/HackerNews.Droid.csproj +++ b/Src/HackNews.Droid/HackerNews.Droid.csproj @@ -9,7 +9,7 @@ Library HackerNews.Droid HackerNews.Droid - v8.1 + v9.0 True Resources\Resource.designer.cs Resource @@ -30,7 +30,7 @@ Xamarin.Android.Net.AndroidClientHandler btls true - armeabi-v7a;armeabi;x86;arm64-v8a;x86_64 + armeabi-v7a;x86;arm64-v8a;x86_64 true diff --git a/Src/HackNews.Droid/Properties/AndroidManifest.xml b/Src/HackNews.Droid/Properties/AndroidManifest.xml index 9a3d8fc..41d7921 100644 --- a/Src/HackNews.Droid/Properties/AndroidManifest.xml +++ b/Src/HackNews.Droid/Properties/AndroidManifest.xml @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/Src/HackerNews/ViewModels/NewsViewModel_GoodAsyncAwaitPractices.cs b/Src/HackerNews/ViewModels/NewsViewModel_GoodAsyncAwaitPractices.cs index 2734669..f445f18 100644 --- a/Src/HackerNews/ViewModels/NewsViewModel_GoodAsyncAwaitPractices.cs +++ b/Src/HackerNews/ViewModels/NewsViewModel_GoodAsyncAwaitPractices.cs @@ -22,7 +22,7 @@ public class NewsViewModel_GoodAsyncAwaitPractices : BaseViewModel #region Properties public IAsyncCommand RefreshCommand => _refreshCommand ?? - (_refreshCommand = new AsyncCommand(ExecuteRefreshCommand, false)); + (_refreshCommand = new AsyncCommand(ExecuteRefreshCommand, continueOnCapturedContext: false)); public List TopStoryList {