Generated Types & Methods
The GeneratedAssembly, GeneratedType, and GeneratedMethod classes form the structural backbone of the JasperFx code generation system. Together they define what classes and methods will be generated, compiled, and optionally persisted.
GeneratedAssembly
GeneratedAssembly is the top-level container. It holds a collection of GeneratedType instances and produces a single C# source file when GenerateCode() is called.
var rules = new GenerationRules("MyApp.Generated");
var assembly = new GeneratedAssembly(rules);Key members:
| Member | Description |
|---|---|
Rules | The GenerationRules governing namespace, type load mode, and variable sources. |
Namespace | The C# namespace for all generated types. |
GeneratedTypes | Read-only list of types added to this assembly. |
AddType(name, baseType) | Creates a new GeneratedType that inherits from or implements the given type. |
GenerateCode() | Arranges all frames and returns the complete C# source code. |
GeneratedType
A GeneratedType represents a single class to be generated. It can inherit from a base class, implement interfaces, and hold injected fields.
Inheriting from a Base Class
public static string BuildGeneratedType()
{
var rules = new GenerationRules("MyApp.Generated");
var assembly = new GeneratedAssembly(rules);
// Create a type that inherits from a base class
var type = assembly.AddType("MyMessageHandler", typeof(MessageHandlerBase));
// The method defined on the base class is discovered automatically.
// Retrieve it by name.
var handleMethod = type.MethodFor("Handle");
// Add frames to define the method body
handleMethod.Frames.Code("Console.WriteLine(\"Handling message...\");");
handleMethod.Frames.Code("return Task.CompletedTask;");
// Generate all source code for the assembly
var code = assembly.GenerateCode();
return code;
}
public abstract class MessageHandlerBase
{
public abstract Task Handle(Message message);
}
public class Message;When a type inherits from a base class, the engine:
- Discovers all overridable methods on the base and adds them as
GeneratedMethodentries. - Reads the base class constructor parameters and registers them as
InjectedFieldentries on the generated type.
Implementing an Interface
public static string ImplementInterface()
{
var rules = new GenerationRules("MyApp.Generated");
var assembly = new GeneratedAssembly(rules);
// Create a type that implements an interface
var type = assembly.AddType("WidgetValidator", typeof(IValidator));
var method = type.MethodFor(nameof(IValidator.Validate));
method.Frames.Code("return {0} != null;", Use.Type<object>());
return assembly.GenerateCode();
}
public interface IValidator
{
bool Validate(object input);
}All methods declared on the interface are added as GeneratedMethod instances that you populate with frames.
Injected Fields
public static string TypeWithInjectedFields()
{
var rules = new GenerationRules("MyApp.Generated");
var assembly = new GeneratedAssembly(rules);
var type = assembly.AddType("NotificationSender", typeof(NotificationSenderBase));
// The InjectedField appears as a constructor argument and private field
var loggerField = new InjectedField(typeof(ILogger));
type.AllInjectedFields.Add(loggerField);
var method = type.MethodFor("Send");
method.Frames.Code("Console.WriteLine(\"Sending notification\");");
return assembly.GenerateCode();
}
public abstract class NotificationSenderBase
{
public abstract void Send(string recipient, string body);
}
public interface ILogger
{
void Log(string message);
}An InjectedField generates:
- A constructor parameter.
- A
private readonlyfield. - An assignment in the constructor body.
Any frame that needs the field's type will resolve to the injected field automatically.
GeneratedMethod
A GeneratedMethod holds the Frames collection that defines the method body. Methods are either discovered from a base class / interface or added manually.
Discovered Methods
When you call assembly.AddType("Name", typeof(SomeBase)), all overridable methods on SomeBase become GeneratedMethod entries. Retrieve them with:
var method = type.MethodFor("Handle");Custom Methods
public static string AddCustomMethod()
{
var rules = new GenerationRules("MyApp.Generated");
var assembly = new GeneratedAssembly(rules);
var type = assembly.AddType("Calculator", typeof(object));
// Add a custom void method
var method = type.AddVoidMethod("PrintSum",
new Argument(typeof(int), "a"),
new Argument(typeof(int), "b"));
method.Frames.Code("Console.WriteLine(a + b);");
// Add a method that returns a value
var multiply = type.AddMethodThatReturns<int>("Multiply",
new Argument(typeof(int), "x"),
new Argument(typeof(int), "y"));
multiply.Frames.Code("return x * y;");
return assembly.GenerateCode();
}AddVoidMethod and AddMethodThatReturns<T> let you define methods that do not come from a base class or interface.
Static Methods
AddStaticVoidMethod emits a static void method. Unlike the other Add*Method calls it also tolerates an empty Frames collection, because a method whose entire payload is its attributes is a legitimate thing to generate — see Rooting pre-generated types.
var companion = assembly.AddType("AotRoots");
var pin = companion.AddStaticVoidMethod("Pin");Attributes
Every GeneratedType and GeneratedMethod carries an Attributes collection of GeneratedAttribute. Attributes are modeled rather than smuggled as raw text so each language writer renders them in its own syntax -- [Foo(...)] in C#, [<Foo(...)>] in F#:
type.Attributes.Add(new GeneratedAttribute(typeof(ObsoleteAttribute), AttributeArg.Value("use the new one")));
method.Attributes.Add(new GeneratedAttribute(typeof(DynamicDependencyAttribute),
AttributeArg.Enum(DynamicallyAccessedMemberTypes.All),
AttributeArg.TypeNamed("MyApp.Generated.SomeGeneratedHandler")));Argument kinds:
| Factory | Emits (C#) | Emits (F#) |
|---|---|---|
AttributeArg.Type(typeof(T)) | typeof(global::Ns.T) | typeof<Ns.T> |
AttributeArg.TypeNamed("Ns.T") | typeof(global::Ns.T) | typeof<Ns.T> |
AttributeArg.Enum(value) | global::Ns.E.Member, combined flags joined with the C# bitwise-or | Ns.E.Member, combined flags joined with the F# bitwise-or |
AttributeArg.Value(literal) | "text" / true / 5L / null | same, with F# numeric literal rules |
AttributeArg.Raw(csharp, fsharp) | verbatim | verbatim |
AttributeArg.TypeNamed is the important one for generated code: a sibling generated type has no runtime Type while codegen write is running, so it can only be referenced by name. Rendering is always fully qualified, so attributes never depend on the using / open statements at the top of the file.
Every generated type is seeded with the [GeneratedCode("JasperFx", "1.0.0")] marker through this same mechanism.
TIP
GeneratedType.Header / GeneratedMethod.Header can still smuggle arbitrary text in front of a declaration, but raw C# attribute text in a Header silently corrupts codegen write --language fsharp output. Prefer Attributes.
Target Language
GeneratedAssembly.TargetLanguage reports which language the assembly is about to be rendered as. DynamicCodeBuilder stamps it before it calls ICodeFile.AssembleTypes, so a code file can vary what it emits by language:
public void AssembleTypes(GeneratedAssembly assembly)
{
var handler = assembly.AddType("SomeHandler", typeof(MessageHandler));
// ...
if (assembly.TargetLanguage == CodegenLanguage.csharp)
{
// C#-only artifact
}
}It defaults to CodegenLanguage.csharp, and the runtime Roslyn path never changes it.
Frames Collection
The Frames property on GeneratedMethod is a FramesCollection. You interact with it primarily through:
Frames.Add(frame)-- add anyFrameinstance.Frames.Code(format, args)-- shorthand to add aCodeFramewith a format string.
During GenerateCode(), the engine arranges frames by resolving variable dependencies, chains them together, and invokes GenerateCode on each frame in order.
GenerationRules
GenerationRules controls assembly-wide settings:
| Property | Description |
|---|---|
GeneratedNamespace | Default namespace for generated types. |
TypeLoadMode | Dynamic, Auto, or Static. See CLI: codegen Command. |
Sources | Collection of IVariableSource implementations for custom variable resolution. |
Assemblies | Referenced assemblies available during compilation. |
