Introducing constructor parameters
Most of the time, when an aspect needs to introduce a parameter to a constructor, 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, the type of the new parameter, and the default value of this parameter. Note that you cannot introduce a parameter without specifying a default value.
The pullAction
parameter of the IntroduceParameter method allows you to specify the value that is passed to this parameter in other constructors calling the specified constructor, using the : this(...)
or : base(...)
syntax. The pullAction
parameter must receive a function that returns a PullAction value. To create a PullAction value, use one of three available static members of this type, such as UseExistingParameter, UseExpression, or IntroduceParameterAndPull.
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 and calls the IInstanceRegistry.Register(this)
method.
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)
{
}
}
}