Skip to content

Commit

Permalink
Merge pull request #3 from brminnick/v1.0
Browse files Browse the repository at this point in the history
V1.0
  • Loading branch information
brminnick authored Nov 30, 2018
2 parents 14e4a8c + c7bbcff commit 3caefec
Show file tree
Hide file tree
Showing 11 changed files with 55 additions and 52 deletions.
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<System.Exception> 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
// ...
Expand All @@ -62,6 +67,22 @@ Allows for `Task` to safely be used asynchronously with `ICommand`:
- `AsyncCommand : IAsyncCommand`
- `IAsyncCommand : ICommand`


```csharp
public AsyncCommand(Func<T, Task> execute,
Func<object, bool> canExecute = null,
Action<Exception> onException = null,
bool continueOnCapturedContext = true)
```

```csharp
public AsyncCommand(Func<Task> execute,
Func<object, bool> canExecute = null,
Action<Exception> onException = null,
bool continueOnCapturedContext = true)
```

```csharp
public class ExampleClass
{
Expand Down
2 changes: 1 addition & 1 deletion Src/AsyncAwaitBestPractices.MVVM.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="2.5">
<id>AsyncAwaitBestPractices.MVVM</id>
<version>0.9.0</version>
<version>1.0.0</version>
<title>Task Extensions for MVVM</title>
<authors>Brandon Minnick, John Thiriet</authors>
<owners>Brandon Minnick</owners>
Expand Down
47 changes: 17 additions & 30 deletions Src/AsyncAwaitBestPractices.MVVM/AsyncCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ public sealed class AsyncCommand<T> : IAsyncCommand
/// Initializes a new instance of the <see cref="T:TaskExtensions.MVVM.AsyncCommand`1"/> class.
/// </summary>
/// <param name="execute">The Function executed when Execute or ExecuteAysnc is called. This does not check canExecute before executing and will execute even if canExecute is false</param>
/// <param name="continueOnCapturedContext">If set to <c>true</c> continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to <c>false</c> continue on a different context; this will allow the Synchronization Context to continue on a different thread</param>
/// <param name="onException">If an exception is thrown in the Task, <c>onException</c> will execute. If onException is null, the exception will be re-thrown</param>
/// <param name="canExecute">The Function that verifies whether or not AsyncCommand should execute.</param>
/// <param name="onException">If an exception is thrown in the Task, <c>onException</c> will execute. If onException is null, the exception will be re-thrown</param>
/// <param name="continueOnCapturedContext">If set to <c>true</c> continue on captured context; this will ensure that the Synchronization Context returns to the calling thread. If set to <c>false</c> continue on a different context; this will allow the Synchronization Context to continue on a different thread</param>
public AsyncCommand(Func<T, Task> execute,
bool continueOnCapturedContext = true,
Func<object, bool> canExecute = null,
Action<Exception> onException = null,
Func<object, bool> canExecute = null)
bool continueOnCapturedContext = true)
{
_execute = execute;
_continueOnCapturedContext = continueOnCapturedContext;
Expand Down Expand Up @@ -71,35 +71,22 @@ public AsyncCommand(Func<T, Task> execute,
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
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
}
Expand All @@ -125,9 +112,9 @@ public sealed class AsyncCommand : IAsyncCommand
/// <param name="onException">If an exception is thrown in the Task, <c>onException</c> will execute. If onException is null, the exception will be re-thrown</param>
/// <param name="canExecute">The Function that verifies whether or not AsyncCommand should execute.</param>
public AsyncCommand(Func<Task> execute,
bool continueOnCapturedContext = true,
Func<object, bool> canExecute = null,
Action<Exception> onException = null,
Func<object, bool> canExecute = null)
bool continueOnCapturedContext = true)
{
_execute = execute;
_continueOnCapturedContext = continueOnCapturedContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public InvalidCommandParameterException(string message) : base(message)
/// <summary>
/// Initializes a new instance of the <see cref="T:TaskExtensions.MVVM.InvalidCommandParameterException"/> class.
/// </summary>
public InvalidCommandParameterException() : base()
public InvalidCommandParameterException()
{

}
Expand Down
6 changes: 3 additions & 3 deletions Src/AsyncAwaitBestPractices.UnitTests/Tests_AsyncCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<int> command = new AsyncCommand<int>(IntParameterTask, continueOnCapturedContext);
AsyncCommand<int> command = new AsyncCommand<int>(IntParameterTask, continueOnCapturedContext: continueOnCapturedContext);
var callingThreadId = Thread.CurrentThread.ManagedThreadId;

//Act
Expand All @@ -123,7 +123,7 @@ public async Task AsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext_
public void AsyncCommand_Parameter_CanExecuteTrue_Test()
{
//Arrange
AsyncCommand<int> command = new AsyncCommand<int>(IntParameterTask, canExecute: CanExecuteTrue);
AsyncCommand<int> command = new AsyncCommand<int>(IntParameterTask, CanExecuteTrue);

//Act

Expand Down
6 changes: 3 additions & 3 deletions Src/AsyncAwaitBestPractices.UnitTests/Tests_IAsyncCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<int>(IntParameterTask, continueOnCapturedContext);
IAsyncCommand command = new AsyncCommand<int>(IntParameterTask, continueOnCapturedContext: continueOnCapturedContext);
var callingThreadId = Thread.CurrentThread.ManagedThreadId;

//Act
Expand All @@ -123,7 +123,7 @@ public async Task IAsyncCommand_ExecuteAsync_Parameter_ContinueOnCapturedContext
public void IAsyncCommand_Parameter_CanExecuteTrue_Test()
{
//Arrange
IAsyncCommand command = new AsyncCommand<int>(IntParameterTask, canExecute: CanExecuteTrue);
IAsyncCommand command = new AsyncCommand<int>(IntParameterTask, CanExecuteTrue);

//Act

Expand Down
2 changes: 1 addition & 1 deletion Src/AsyncAwaitBestPractices.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="2.5">
<id>AsyncAwaitBestPractices</id>
<version>0.9.0</version>
<version>1.0.0</version>
<title>Task Extensions for System.Threading.Tasks</title>
<authors>Brandon Minnick, John Thiriet</authors>
<owners>Brandon Minnick</owners>
Expand Down
11 changes: 3 additions & 8 deletions Src/AsyncAwaitBestPractices/TaskExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Src/HackNews.Droid/HackerNews.Droid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<OutputType>Library</OutputType>
<RootNamespace>HackerNews.Droid</RootNamespace>
<AssemblyName>HackerNews.Droid</AssemblyName>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<TargetFrameworkVersion>v9.0</TargetFrameworkVersion>
<AndroidApplication>True</AndroidApplication>
<AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
<AndroidResgenClass>Resource</AndroidResgenClass>
Expand All @@ -30,7 +30,7 @@
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<AndroidTlsProvider>btls</AndroidTlsProvider>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
<AndroidSupportedAbis>armeabi-v7a;armeabi;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a;x86_64</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down
2 changes: 1 addition & 1 deletion Src/HackNews.Droid/Properties/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.minnick.HackerNews">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="27" />
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="28" />
<application android:label="HackerNews"></application>
</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -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<StoryModel> TopStoryList
{
Expand Down

0 comments on commit 3caefec

Please sign in to comment.