Metalama 1.0 / / Metalama Documentation / Conceptual Documentation / Creating Aspects / Transforming Code / Introducing Constructor Parameters

Introducing Constructor Parameters

Most of the times, when an aspects needs to introduce a constructor to a parameter, it is because it needs to pull a dependency from a dependency injection framework. In this situation, it is recommended to use the Metalama.Extensions.DependencyInjection framework described in Injecting Dependencies Into Aspects.

Implementations of dependency injection frameworks typically introduce parameters using the method described here.

To append a parameter to a constructor, use the IntroduceParameter method. This method requires several arguments: the target IConstructor, the name and type of the new parameter, as well as the default value of this parameter. Note that it is currently not allowed to introduce a parameter without specifying a default value.

The pullAction parameter of the IntroduceParameter method deserves attention: it allows you to specify the value passed to this parameter in other constructors calling the specified construtor, using the : this(...) or : base(...) syntax. The pullAction parameter must receive a function that returns a PullAction value. You can create a PullAction value using one of the following static methods of this type:

  • UseExistingParameter to use an existing parameter of the calling constructor,
  • UseExpression to pass an arbitrary expresssion,
  • IntroduceParameterAndPull to introduce a new parameter into the calling constructor. In this case, the pullAction function is called again, recursively, for each constructor calling this new constructor.

Example

The following example shows 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, then calls the IInstanceRegistry.Register(this).

using Metalama.Framework.Advising;
using Metalama.Framework.Aspects;
using Metalama.Framework.Code;
using Metalama.Framework.Code.SyntaxBuilders;

namespace Doc.IntroduceParameter
{
    internal class RegisterInstanceAttribute : ConstructorAspect
    {
        public override void BuildAspect( IAspectBuilder<IConstructor> builder )
        {

            builder.Advice.IntroduceParameter(
                builder.Target,
                "instanceRegistry",
                typeof( IInstanceRegistry ),
                TypedConstant.Default( typeof( IInstanceRegistry ) ),
                pullAction: ( parameter, constructor ) =>
                 PullAction.IntroduceParameterAndPull(
                    "instanceRegistry",
                    TypeFactory.GetType( typeof( IInstanceRegistry ) ),
                    TypedConstant.Default( typeof( IInstanceRegistry ) ) ) );

            builder.Advice.AddInitializer( builder.Target, StatementFactory.Parse( "instanceRegistry.Register( this );" ) );

        }
    }

    public interface IInstanceRegistry
    {
        void Register( object instance );
    }

}
namespace Doc.IntroduceParameter
{
    internal class Foo
    {
        [RegisterInstance]
        public Foo()
        {

        }

    }

    internal class Bar : Foo
    {

    }

}
namespace Doc.IntroduceParameter
{
    internal class Foo
    {
        [RegisterInstance]
        public Foo(IInstanceRegistry instanceRegistry = default)
        {
            instanceRegistry.Register(this);
        }
    }

    internal class Bar : Foo
    {
        public Bar(IInstanceRegistry instanceRegistry = default) : base(instanceRegistry)
        {
        }
    }

}