MetalamaConceptual documentationCreating aspectsAdvising codeIntroducing constructor parameters
Open sandboxFocusImprove this doc

Introducing constructor parameters

Most of the time, an aspect requires the introduction of a parameter to a constructor when it needs to retrieve a dependency from a dependency injection framework. In such situations, it is advisable to utilize the Metalama.Extensions.DependencyInjection framework, as detailed in Injecting dependencies into aspects.

Typically, implementations of dependency injection frameworks introduce parameters using the method outlined here.

To append a parameter to a constructor, the IntroduceParameter method is used. This method necessitates several arguments: the target IConstructor, the name, the type of the new parameter, and the default value of this parameter. It's important to note that a parameter cannot be introduced without specifying a default value.

The pullAction parameter of the IntroduceParameter method allows the specification of the value that is passed to this parameter in other constructors calling the specified constructor, utilizing the : this(...) or : base(...) syntax. The pullAction parameter must receive a function that returns a PullAction value. To create a PullAction value, one of three available static members of this type should be used, such as UseExistingParameter, UseExpression, or IntroduceParameterAndPull.

Example

The below example demonstrates an aspect that registers the current instance in a registry of type IInstanceRegistry. The aspect appends a parameter of type IInstanceRegistry to the target constructor and invokes the IInstanceRegistry.Register(this) method.

1using Metalama.Framework.Advising;
2using Metalama.Framework.Aspects;
3using Metalama.Framework.Code;
4using Metalama.Framework.Code.SyntaxBuilders;
5
6namespace Doc.IntroduceParameter
7{
8    internal class RegisterInstanceAttribute : ConstructorAspect
9    {
10        public override void BuildAspect( IAspectBuilder<IConstructor> builder )
11        {
12            builder.Advice.IntroduceParameter(
13                builder.Target,
14                "instanceRegistry",
15                typeof(IInstanceRegistry),
16                TypedConstant.Default( typeof(IInstanceRegistry) ),
17                pullAction: ( parameter, constructor ) =>
18                    PullAction.IntroduceParameterAndPull(
19                        "instanceRegistry",
20                        TypeFactory.GetType( typeof(IInstanceRegistry) ),
21                        TypedConstant.Default( typeof(IInstanceRegistry) ) ) );
22
23            builder.Advice.AddInitializer( builder.Target, StatementFactory.Parse( "instanceRegistry.Register( this );" ) );
24        }
25    }
26
27    public interface IInstanceRegistry
28    {
29        void Register( object instance );
30    }
31}
Source Code
1namespace Doc.IntroduceParameter
2{
3    internal class Foo
4    {
5        [RegisterInstance]
6        public Foo() { }
7    }
8



9    internal class Bar : Foo { }
10}
Transformed Code
1namespace Doc.IntroduceParameter
2{
3    internal class Foo
4    {
5        [RegisterInstance]
6        public Foo(IInstanceRegistry instanceRegistry = default)
7        {
8            instanceRegistry.Register(this);
9        }
10    }
11
12    internal class Bar : Foo
13    {
14        public Bar(IInstanceRegistry instanceRegistry = default) : base(instanceRegistry)
15        {
16        }
17    }
18}