| Severity | Error |
| Reported by | Metalama.Framework |
| Message | '<Symbol>' is invalid because '<RunTimeSymbol>' is run-time but '<CompileTimeSymbol>' is compile-time. |
Cause
Metalama assigns an execution scope to every type and member. Compile-time types, like the code model types IMethod or IParameter, exist only while Metalama compiles the project. Run-time types, like the classes of your project, exist only in the compiled program. Some types, like string or Dictionary<TKey, TValue>, are available in both scopes, and the scope of a generic type instance depends on its type arguments.
Metalama reports LAMA0226 when a construct depends on both a run-time-only element and a compile-time-only element, so that it can exist in neither scope. The most common cases are:
- a generic type or method instance whose type arguments include a run-time-only type and a compile-time-only type, for instance
Dictionary<IParameter, Customer>, or a call to meta.CompileTime with a run-time type argument; - a type that has both a run-time-only and a compile-time-only base type or interface.
The first placeholder of the message is the invalid construct, the second one is the run-time element, and the third one is the compile-time element.
In the following example, the template declares a dictionary that maps each compile-time parameter, IParameter, to a run-time AuditEntry object.
1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System.Collections.Generic;
4
5namespace Doc.LAMA0226.Error;
6
7public class AuditAttribute : OverrideMethodAspect
8{
9 public override dynamic? OverrideMethod()
10 {
11 // Error: the type combines the compile-time IParameter and the run-time AuditEntry.
Error LAMA0226: 'Dictionary<IParameter, AuditEntry>' is invalid because 'AuditEntry' is run-time but 'IParameter' is compile-time.
12 Dictionary<IParameter, AuditEntry> entries = new();
13
14 return meta.Proceed();
15 }
16}
17
1using System;
2using System.Collections.Generic;
3
4namespace Doc.LAMA0226.Error;
5
6public class AuditEntry
7{
8 public AuditEntry( object? value )
9 {
10 this.Value = value;
11 }
12
13 public object? Value { get; }
14}
15
16public static class AuditLog
17{
18 public static void Write( string methodName, Dictionary<string, AuditEntry> entries )
19 => Console.WriteLine( $"{methodName} called with {entries.Count} argument(s)." );
20}
21
22internal class OrderService
23{
24 [Audit]
25 public void PlaceOrder( string product, int quantity ) { }
26}
27
Resolution
Decide whether the construct is compile-time or run-time, and replace the elements of the other scope:
- For a run-time construct, replace the compile-time elements with run-time values. For instance, use the name of the parameter, a
string, instead of the IParameter, as shown in the following example. - For a compile-time construct, replace the run-time types with compile-time representations, for instance an IType or IExpression instead of a run-time type.
- For a type with conflicting base types, split it into a run-time type and a compile-time type.
1using Metalama.Framework.Aspects;
2using System.Collections.Generic;
3
4namespace Doc.LAMA0226.Fixed;
5
6public class AuditAttribute : OverrideMethodAspect
7{
8 public override dynamic? OverrideMethod()
9 {
10 // Fixed: the dictionary is keyed by the parameter name, so all its type arguments are run-time.
11 var entries = new Dictionary<string, AuditEntry>();
12
13 foreach ( var parameter in meta.Target.Parameters )
14 {
15 entries.Add( parameter.Name, new AuditEntry( parameter.Value ) );
16 }
17
18 AuditLog.Write( meta.Target.Method.Name, entries );
19
20 return meta.Proceed();
21 }
22}
23
1using System;
2using System.Collections.Generic;
3
4namespace Doc.LAMA0226.Fixed;
5
6public class AuditEntry
7{
8 public AuditEntry( object? value )
9 {
10 this.Value = value;
11 }
12
13 public object? Value { get; }
14}
15
16public static class AuditLog
17{
18 public static void Write( string methodName, Dictionary<string, AuditEntry> entries )
19 => Console.WriteLine( $"{methodName} called with {entries.Count} argument(s)." );
20}
21
22internal class OrderService
23{
24 [Audit]
25 public void PlaceOrder( string product, int quantity ) { }
26}
27
1using System;
2using System.Collections.Generic;
3
4namespace Doc.LAMA0226.Fixed;
5
6public class AuditEntry
7{
8 public AuditEntry(object? value)
9 {
10 this.Value = value;
11 }
12
13 public object? Value { get; }
14}
15
16public static class AuditLog
17{
18 public static void Write(string methodName, Dictionary<string, AuditEntry> entries)
19 => Console.WriteLine($"{methodName} called with {entries.Count} argument(s).");
20}
21
22internal class OrderService
23{
24 [Audit]
25 public void PlaceOrder(string product, int quantity)
26 {
27 var entries = new Dictionary<string, AuditEntry>();
28 entries.Add("product", new AuditEntry(product));
29 entries.Add("quantity", new AuditEntry(quantity));
30 AuditLog.Write("PlaceOrder", entries);
31 }
32}
33