The aim of this tool is to improve the Developer Experience of writing C# source generators, as well as the final User Experience. It enables the usage of reflection in alternative of the semantic model and the execution of the user's code without compiling it. This means users can configure the behavior of the generator with code, instead of, say, attributes.
Make your generator class inherit from RuntimeSourceGenerator
instead of implementing the ISourceGenerator
interface, then override the Execute
and Initialize
methods. The Initialize
method is the same method you would implement normally, while the Execute
method contains an extra parameter - an instance of IGeneratorRuntime
.
using GeneratorKit;
[Generator]
public class MySourceGenerator : RuntimeSourceGenerator
{
public override void Execute(GeneratorExecutionContext context, IGeneratorRuntime runtime)
{
// Execution logic goes here
}
public override void Initialize(GeneratorInitializationContext context)
{
// Initialization logic goes here
}
}
The IGeneratorRuntime
interface exposes a CompilationAssembly
property which represents the assembly of the current compilation. This assembly can be used to query types and most of the things you would normally do with an Assembly
object.
Another thing you can do with the IGeneratorRuntime
is creating reflection delegators from ISymbol
's. For example:
ITypeSymbol myTypeSymbol = ...;
Type myType = runtime.CreateTypeDelegator(myType);
Same goes for IPropertySymbol
/PropertyInfo
, IMethodSymbol
/MethodInfo
, and so on.
Note: This is how this feature should look like when finished.
Once you got your type delegator, you can use the activator class to instantiate it.
Suppose you have a base class GeneratorConfiguration
which exposes a set of methods to configure the source generator. So, the user would extend this class and use those methods, say, in the class constructor:
public class MyGeneratorConfiguration : GeneratorConfiguration
{
public MyGeneratorConfiguration()
{
// Configuration logic goes here
}
}
The source generator can instantiate this class (or, rather, a class that mirrors it) and then access the result of the user's action.
Type configurationType = runtime.CompilationAssembly
.GetTypes()
.FirstOrDefault(t => t.BaseType.Equals(typeof(GeneratorConfiguration)));
if (configurationType == null)
{
return;
}
GeneratorConfiguration configuration = (GeneratorConfiguration)Activator.CreateInstance(configurationType);
// Do stuff with your instance