Generates synchronized method from async method
$ dotnet add package Zomp.SyncMethodGeneratorThis .NET source generator produces a sync method from an async one.
⭐ Star this project if you hate code duplication or calling async methods from sync.
Decorate your async method with CreateSyncVersionAttribute in your partial class, struct, record, or interface:
[Zomp.SyncMethodGenerator.CreateSyncVersion]
static async Task WriteAsync(ReadOnlyMemory<byte> buffer, Stream stream,
CancellationToken ct)
=> await stream.WriteAsync(buffer, ct).ConfigureAwait(true);
and it will generate a sync version of the method:
static void Write(ReadOnlySpan<byte> buffer, Stream stream)
=> stream.Write(buffer);
A list of changes applied to the new synchronized method:
Remove async modifier
Remove await from methods as well as foreach statement
Change types
* ValueTasks are handled exactly like Tasks
** Memory and ReadOnlyMemory is preserved in sync methods if it is a type argument of a collection. This is due to a compiler limitation which states that a ref struct can't be the element type of an array.
Remove parameters
PreserveCancellationToken property is set to true.PreserveProgress property is set to true.Invocation changes
ConfigureAwait from Tasks and Asynchronous EnumerationsConfigureAwait statementsWaitAsync from TasksAsync suffix to call synchronous version (e.g. MoveNextAsync() becomes MoveNext())Async suffixawait Task<TResult>.FromResult(value) to valueawait Task.Delay(value) to Thread.Sleep(value)Remove CreateSyncVersionAttribute
Update XML documentation
This source generator detects language version during the compilation. By default it will generate #nullable enable directive if and only if the language version is 8 or above. Since it is impossible to reliably determine whether nullable context is turned on or not, OmitNullableDirective property is available to omit that directive from generating.
[Zomp.SyncMethodGenerator.CreateSyncVersion(OmitNullableDirective = true)]
public async Task MethodAsync()
{
string f = null;
}
By default, this source generator removes IProgress<T> parameters from async methods. To preserve them, use the PreserveProgress option.
[Zomp.SyncMethodGenerator.CreateSyncVersion(PreserveProgress = true)]
public async Task MethodAsync(IProgress<double> progress)
{
progress.Report(0.0);
}
By default, this source generator removes CancellationToken parameters from async methods. To preserve them, use the PreserveCancellationToken option.
[Zomp.SyncMethodGenerator.CreateSyncVersion(PreserveCancellationToken = true)]
public async Task MethodAsync(CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
}
You can also decorate your type (class, struct, record, or interface) to generate a sync version for every asynchronous method.
[Zomp.SyncMethodGenerator.CreateSyncVersion]
partial class MyClass {
async Task Method1Async(...) { ... }
async IAsyncEnumerable<...> Method2Async(...) { ... }
[Zomp.SyncMethodGenerator.SkipSyncVersion]
async Task WillNotGenerateAsync(...) { ... }
}
This will generate their sync counterparts:
[Zomp.SyncMethodGenerator.CreateSyncVersion]
partial class MyClass {
void Method1(...) { ... }
}
and
[Zomp.SyncMethodGenerator.CreateSyncVersion]
partial class MyClass {
IEnumerable<...> Method2(...) { ... }
}
To exclude a method from generating a sync version use SkipSyncVersionAttribute on a method. See WillNotGenerateAsync method in the example above.
In case there is logic which should only be executed in the synchronized version of the method, wrap it in SYNC_ONLY #if directive.
SYNC_ONLY must not be defined anywhere. The source generator will scan #if directives for this symbol.
Code inside SYNC_ONLY block will be copied as is. Unless global namespaces are used in the project, this code should contain fully qualified namespaces.
The following syntax:
[Zomp.SyncMethodGenerator.CreateSyncVersion]
public async Task WithSyncOnlyDirectiveAsync(CancellationToken ct)
{
#if SYNC_ONLY
System.Console.Write("Sync");
#endif
await Task.CompletedTask;
}
will output:
public void WithSyncOnlyDirective()
{
System.Console.Write("Sync");
}
If you only want to execute in the original async version, flip the flag like this: #if !SYNC_ONLY.
Note: SYNC_ONLY cannot be mixed with other symbols in a conditional expression and cannot have #elif directive.
[!WARNING]
SYNC_ONLYflag currently works in parameter lists, argument lists and statements.
Please always double check your code when using this flag.
If your use case is not supported, please log an issue.
To add the library use:
dotnet add package Zomp.SyncMethodGenerator
This project is fully compatible with act.
Other than required packages to run act itself, GitHub Actions script installs anything else that might be missing, such as node, yarn and dotnet. On Windows platform, software installation is performed on the host itself due to lack of container support.
To build the project using act follow these instructions:
Install chocolatey if missing.
Install the following packages if missing:
choco install git -y
choco install act-cli -y
refreshenv
In the project directory run:
act -P windows-latest=-self-hosted --artifact-server-path c:/tmp/artifacts
Install act by following these instructions.
In the project directory run:
act --artifact-server-path /tmp/artifacts