Open sandboxFocusImprove this doc

LAMA0227: dynamic can't be used to build another type in a template

Severity Error
Reported by Metalama.Framework
Message The type '<Type>' is forbidden in a template: 'dynamic' cannot be used as a generic argument type, an array element type, a tuple element type or a ref type.

Cause

In a T# template, dynamic stands for a run-time value or type that Metalama doesn't know when it compiles the aspect. When Metalama expands the template, it replaces each dynamic expression with the code of the actual expression. This works only when dynamic is used directly as the type of an expression, a local variable, or a template return value.

Metalama reports LAMA0227 when a template uses dynamic as a component of another type: as a generic argument (List<dynamic>), as an array element type (dynamic[], new dynamic[5]), as a tuple element type ((dynamic, int)), or with ref (ref dynamic). The placeholder of the message is the offending type, as written in the template.

In the following example, the aspect copies the argument values of the target method, obtained with meta.Target.Parameters.ToValueArray(), into a List<dynamic?>.

1using Metalama.Framework.Aspects;
2using System;
3using System.Collections.Generic;
4
5namespace Doc.LAMA0227.Error;
6
7public class LogArgumentsAttribute : OverrideMethodAspect
8{
9    public override dynamic? OverrideMethod()
10    {
11        // Error: dynamic cannot be used as a generic argument in a template.
        Error LAMA0227: The type 'List<dynamic?>' is forbidden in a template: 'dynamic' cannot be used as a generic argument type, an array element type, a tuple element type or a ref type.

12        var values = new List<dynamic?>( meta.Target.Parameters.ToValueArray() );
13
14        Console.WriteLine( $"{meta.Target.Method.Name}({string.Join( ", ", values )})" );
15
16        return meta.Proceed();
17    }
18}
19
1namespace Doc.LAMA0227.Error;
2
3internal class Calculator
4{
5    [LogArguments]
6    public int Add( int a, int b ) => a + b;
7}
8

Resolution

Replace dynamic with a type that the generated code can use:

  • Use object or object? when the values only need to be stored or passed along, as in the following example. A dynamic expression converts implicitly to object.
  • Use the actual type when it's known, or a generic template parameter when it depends on the target declaration.
  • To pass all arguments of the target method as an array, use meta.Target.Parameters.ToValueArray() directly. It generates an object[] expression.

1using Metalama.Framework.Aspects;
2using System;
3using System.Collections.Generic;
4
5namespace Doc.LAMA0227.Fixed;
6
7public class LogArgumentsAttribute : OverrideMethodAspect
8{
9    public override dynamic? OverrideMethod()
10    {
11        // Fixed: the list stores the argument values as object.
12        var values = new List<object?>( meta.Target.Parameters.ToValueArray() );
13
14        Console.WriteLine( $"{meta.Target.Method.Name}({string.Join( ", ", values )})" );
15
16        return meta.Proceed();
17    }
18}
19
Source Code
1namespace Doc.LAMA0227.Fixed;
2



3internal class Calculator
4{
5    [LogArguments]
6    public int Add( int a, int b ) => a + b;
7}
8
Transformed Code
1using System;
2using System.Collections.Generic;
3
4namespace Doc.LAMA0227.Fixed;
5
6internal class Calculator
7{
8    [LogArguments]
9    public int Add(int a, int b)
10    {
11        var values = new List<object?>(new object[] { a, b });
12        Console.WriteLine($"Add({string.Join(", ", values)})");
13        return a + b;
14    }
15}
16

See also