Compile-Time vs Run-Time Code
Scopes of code
A fundamental concept of Metalama is that any type of your source code belongs to one of the following scopes:
Run-time code is the code that you are used to: it compiles to a binary assembly and typically executes on the end user's device. In a project that does not reference Metalama.Framework, all code is considered run-time.
The entry point of run-time code is typically the Program.Main method.
Compile-time code is code that is executed either at compile time by the compiler, or at design time by the IDE.
Metalama recognizes compile-time-only code thanks to the CompileTimeAttribute custom attribute. It will look at the attribute on the member, on the declaring type, and at the base types and interfaces. Most classes and interfaces of the Metalama.Framework assembly are compile-time-only.
You can create compile-time classes by annotating them with CompileTimeAttribute.
All compile-time code must be strictly compatible with .NET Standard 2.0, even if the containing project targets a richer platform. We will see why in a minute.
There are two kinds of entry points for compile-time code:
- Aspects which are added to source code as custom attributes, and
- Fabrics which are executed just because they exist.
Scope-neutral code is code that can execute either at run time or at compile time.
Scope-neutral code is annotated with the RunTimeOrCompileTimeAttribute custom attribute.
Aspect classes are scope-neutral because aspects are a special kind of class. Aspects are typically represented as custom attributes, and these attributes can be accessed at run time using System.Reflection, but they are also instantiated at compile time by Metalama. Therefore, it is important that the constructors and public properties of the aspects are both run-time and compile-time.
However, some methods of aspect classes are purely compile-time. They cannot be executed at run time because they access APIs that exist only at compile time. These methods are annotated with CompileTimeAttribute or one of the other derived attribute classes.
When Metalama compiles your project, one of the first steps is to separate the compile-time code from the run-time code. From your initial project, Metalama creates two compilations:
- The compile-time compilation contains only compile-time code. It is compiled against .NET Standard 2.0. It is then loaded within the compiler or IDE process, and executed at compile or design time.
- The run-time compilation contains the run-time code. It also contains the compile-time declarations, but their implementation is replaced by a
During compilation, Metalama compiles the T# templates into standard C# code that generates the run-time code using the Roslyn API. This generated code, as well as any non-template compile-time code, is then zipped and embedded in the run-time assembly as a managed resource.
Intellectual property alert. The source of your compile-time code is embedded in clear text, without any obfuscation, in the run-time binary assemblies as a managed resource.