NoWoL.SourceGenerators
0.1.29
dotnet add package NoWoL.SourceGenerators --version 0.1.29
NuGet\Install-Package NoWoL.SourceGenerators -Version 0.1.29
<PackageReference Include="NoWoL.SourceGenerators" Version="0.1.29" />
paket add NoWoL.SourceGenerators --version 0.1.29
#r "nuget: NoWoL.SourceGenerators, 0.1.29"
// Install NoWoL.SourceGenerators as a Cake Addin #addin nuget:?package=NoWoL.SourceGenerators&version=0.1.29 // Install NoWoL.SourceGenerators as a Cake Tool #tool nuget:?package=NoWoL.SourceGenerators&version=0.1.29
NoWoL.SourceGenerators
NoWoL.SourceGenerators will contain C# (.NET 6/VS2022) source generators used to improve a developer's life. So far it includes:
- a way to generate the boilerplate for an exception:
ExceptionGenerator
- an simple async to sync code generator:
AsyncToSyncConverterGenerator
Installation
Use your favorite Nuget package manager to add the NoWoL.SourceGenerators
package to your project. Once installed you need to edit the reference to the package in the csproj file to add these attributes:
PrivateAssets="all"
- This attribute means that projects referencing this one won't get a reference to the package
ExcludeAssets="runtime"
- This attribute means that the package's DLL is not copied to your build output
You should end up with something similar to this:
<ItemGroup>
<PackageReference Include="NoWoL.SourceGenerators" Version="0.1.16"
PrivateAssets="all" ExcludeAssets="runtime" />
</ItemGroup>
ExceptionGenerator Usage
Define a partial class decorated with the [ExceptionGenerator]
attribute to generate the boilerplate code of the exception. The exception always inherits from System.Exception
. While it would be possible to support inheriting from another exception type, it would be too problematic to programmatically call the correct constructor of the base class.
It is possible to define a default message for the exception and doing so will generate a Create
helper method to create an instance of the exception using the message. It is also possible to define parameters values in the message or parameter formatter. A parameter formatter is simply a static method that will be called using the parameter value and will returned a processed value, usually a string, that will be included in the message. A type of parameter formatter could be a function that takes an IEnumerable
value and formats it as a comma separated list.
[ExceptionGenerator]
public partial class SampleException { }
[ExceptionGenerator("This is the exception's message")]
public partial class SampleWithMessageException { }
[ExceptionGenerator("Hello my name is {string theParameterName}")]
public partial class SampleWithMessageAndParameterException { }
// The parameter formatter is defined between angle brackets <>.
[ExceptionGenerator("This is a message with a custom parameter formatter {<SomeNameSpace.SomeClass.SomeStaticMethod>string theParameterName}")]
public partial class SampleWithCustomFormatterException { }
...
// Different ways to throw the exceptions
public void Main()
{
throw new SampleException();
throw SampleWithMessageException.Create(); // throw using the default message
throw new SampleWithMessageException("Another message"); // you're not limited to the default message, you can redefine it at runtime
throw SampleWithMessageAndParameterException.Create("Jeff"); // This will generate the message 'Hello my name is Jeff'
throw SampleWithCustomFormatterException.Create("param value"); // This is similar to the previous line however the value will be modified by the formatter before being included in the message
}
AlwaysInitializedProperty Usage
Define a partial class with one or more fields decorated with the [AlwaysInitializedProperty]
attribute to generate the boilerplate code to create a property which initialize its backing field when the field is null. This ensure that the property will never return null.
public partial class TestClass
{
[AlwaysInitializedProperty]
private List<int> _field1;
}
This will generate this code:
public partial class TestClass
{
public List<int> Field1
{
get
{
if (_field1 == default)
{
_field1 = new List<int>();
}
return _field1;
}
set { _field1 = value; }
}
}
AsyncToSyncConverterGenerator Usage
Experimental generator uses simple rules to convert async code to their sync version:
- Caching of the generated code, used by Visual Studio, can be a bit wonky.
- The name of the method must end with
Async
. The generator does not validate if a non-async version of the method exists. - The generated method will have the same access modifiers as the original method.
- Awaitable local functions must end with
Async
. The generator does not validate if a non-async version of the location function exists. - Identifiers (e.g.: variable name) ending with
Async
will haveAsync
removed from their name. - Assumes that a
Func<>
returning aTask
will be awaited. In this case theFunc<>
will be converted to anAction<>
which will not be awaited. - ConfigureAwait / ConfigureAwaitWithCulture will be removed.
Task.Delay
is converted toThread.Sleep
.- Type conversion:
Task
is converted tovoid
Task<T>
is converted toT
ValueTask
is converted tovoid
ValueTask<T>
is converted toT
Func<Task>
is converted toAction
Func<int, Task>
is converted toAction<int>
Func<int, Task<double>>
is converted toFunc<int, double>
- Async streams will be converted to sync by dropping their
await
keyword and removingAsync
from the name of their method. - Attributes and XML documentation will be copied to the new method.
- The cancellation token parameter should be named
cancellationToken
otherwise it will not be removed from XML documentation.
- The cancellation token parameter should be named
Not yet supported:
- Creating a
Task
variable and returning it.
To use it you need to define an async method decorated with the [AsyncToSyncConverter]
attribute to generate a sync version of the method. The method must be inside a partial class otherwise it is not possible to add new code to the class.
For example, the following code
public partial class TestClass
{
[AsyncToSyncConverter]
public async Task MainMethodAsync(CancellationToken ct)
{
await TheMethodAsync();
await TheMethodAsync().ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(30), ct);
}
public async Task TheMethodAsync()
{
// omitted
}
public void TheMethod()
{
// omitted
}
}
will be converted to
public partial class TestClass
{
public void MainMethod(CancellationToken ct)
{
TheMethod();
TheMethod();
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(30));
}
}
It is also possible to use this generator to generate the sync methods of an interface.
For example, the following code
public partial interface IMyInterface
{
[AsyncToSyncConverter]
Task<int> MainMethodAsync(int someValue);
}
will be converted to
public partial interface IMyInterface
{
int MainMethod(int someValue);
}
Bug Reports
Please include the smallest code possible to reproduce the issue.
Contributing
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
Please make sure to update tests as appropriate.
License
References
Useful information for creating source generators:
- Presentation by Andrey Dyatlov: https://youtu.be/052xutD86uI
- List of existing source generators: https://github.com/amis92/csharp-source-generators
- https://www.thinktecture.com/en/net/roslyn-source-generators-performance/
- https://andrewlock.net/creating-a-source-generator-part-1-creating-an-incremental-source-generator/
- https://andrewlock.net/exploring-dotnet-6-part-9-source-generator-updates-incremental-generators/
- https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md
- Great collection of analyzers to help figure out how syntax trees work: https://github.com/JosefPihrt/Roslynator
- Tool to see a syntax tree: https://sharplab.io
- Tool to generate Roslyn API calls: https://roslynquoter.azurewebsites.net/
Learn more about Target Frameworks and .NET Standard.
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
0.1.29 | 330 | 1/29/2023 |
0.1.28 | 308 | 1/17/2023 |
0.1.27 | 322 | 1/14/2023 |
0.1.26 | 305 | 1/13/2023 |
0.1.25 | 303 | 1/13/2023 |
0.1.24 | 292 | 1/9/2023 |
0.1.23 | 299 | 1/6/2023 |
0.1.22 | 345 | 12/4/2022 |
0.1.21 | 454 | 8/20/2022 |
0.1.20 | 413 | 8/20/2022 |
0.1.19 | 406 | 8/20/2022 |
0.1.18 | 520 | 4/10/2022 |
0.1.16 | 731 | 4/10/2022 |
0.1.14 | 420 | 3/30/2022 |
0.1.13 | 412 | 3/29/2022 |
0.1.12 | 383 | 3/28/2022 |
0.1.11 | 445 | 3/28/2022 |
0.1.10 | 425 | 3/13/2022 |
0.1.9 | 431 | 3/12/2022 |
0.1.8 | 427 | 3/12/2022 |
0.1.7 | 453 | 3/2/2022 |