MetalamaConceptual documentationCreating aspectsAdvising codeSharing state with advice
Open sandboxFocusImprove this doc

Sharing state with advice

When you need to share compile-time state between different pieces of advice, or between your implementation of the BuildAspect method and the advice, there are several strategies available to you.

Note

If you need to share run-time state with advice, a different strategy must be adopted. For instance, you could introduce a field in the target type and utilize it from several advice methods.

Warning

DO NOT share state with an aspect field if that state depends on the target declaration of the aspect. In scenarios involving inherited aspects or cross-project validators, the same instance of the aspect class will be reused across all inherited targets. Always design aspects as immutable classes.

Sharing state with compile-time template parameters

This is the most direct approach for passing values from your BuildAspect method to a template method. However, it is only applicable with method templates. For more details, refer to Template parameters and type parameters.

Sharing state with the Tags property

Compile-time template parameters are not available for event, property, or field templates. A straightforward alternative is to use tags, which are arbitrary name-value pairs.

To define and use tags:

  1. In your implementation of the BuildAspect method, when adding the advice by calling a method of the IAdviceFactory interface, pass the tags as an anonymous object to the tags argument. For example, tags: new { A = 5, B = "x", C = builder.Target.DeclaringType }. In this instance, A, B, and C are three arbitrary names.

  2. In your template method, the tags are accessible under the meta.Tags dictionary. For instance, you would use the meta.Tags["A"] expression to access the tag named A that you defined in the previous step.

Example

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
4
5namespace Doc.Tags
6{
7    internal class TagsAspect : MethodAspect
8    {
9        public override void BuildAspect( IAspectBuilder<IMethod> builder )
10        {
11            builder.Advice.Override(
12                builder.Target,
13                nameof(this.OverrideMethod),
14                tags: new { ParameterCount = builder.Target.Parameters.Count } );
15        }
16
17        [Template]
18        private dynamic? OverrideMethod()
19        {
20            Console.WriteLine( $"This method has {meta.Tags["ParameterCount"]} parameters." );
21
22            return meta.Proceed();
23        }
24    }
25}
Source Code
1using System;
2
3namespace Doc.Tags
4{
5    internal class Foo
6    {
7        [TagsAspect]
8        private void Bar( int a, int b )
9        {
10            Console.WriteLine( $"Method({a}, {b})" );
11        }
12    }

13}
Transformed Code
1using System;
2
3namespace Doc.Tags
4{
5    internal class Foo
6    {
7        [TagsAspect]
8        private void Bar(int a, int b)
9        {
10            Console.WriteLine("This method has 2 parameters.");
11            Console.WriteLine($"Method({a}, {b})");
12        }
13    }
14}

Sharing state with the AspectState property

You can utilize the IAspectBuilder.AspectState property to store any aspect state that depends on the target declaration. This object is exposed on the IAspectInstance.AspectState property and is visible to child aspects and aspects that inherit from them.