MetalamaConceptual documentationCreating aspectsAdvising codeAdding and removing attributes
Open sandboxFocusImprove this doc

Adding custom attributes

An aspect can add or remove custom attributes to or from any declaration. There are several ways to accomplish this, depending on whether the declaration exists in the code model or is being added by the aspect.

Adding attributes to an existing declaration

To add a custom attribute to a declaration that exists before the aspect is applied, use the IntroduceAttribute method. This method requires an argument of type IAttributeData. This interface is implemented by the IAttribute interface, allowing you to introduce any custom attribute that you find in the code model via the IDeclaration.Attributes property. To create a new custom attribute, use the AttributeConstruction.Create method. This method requires the attribute type or constructor along with two optional sets of arguments: constructor arguments are the arguments of the constructor, and named arguments are the values assigned to fields and properties.

Example: adding EditorBrowsableAttribute to fields

The following aspect adds <xref:System.ComponentModel.EditorBrowsableAttribute> to all fields whose name starts with a double underscore.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.ComponentModel;
5using System.Linq;
6
7#pragma warning disable CA1310 // Specify StringComparison for correctness
8
9namespace Doc.AddEditorBrowsableAttribute
10{
11    public class HideFieldsFromEditorAttribute : TypeAspect
12    {
13        public override void BuildAspect( IAspectBuilder<INamedType> builder )
14        {
15            // Construct a custom attribute.
16            var attribute = AttributeConstruction.Create(
17                typeof(EditorBrowsableAttribute),
18                new object[] { EditorBrowsableState.Never } );
19
20            // Add a copy of it to each field whose name starts with a double underscore.
21            foreach ( var field in builder.Target.Fields
22                         .Where( f => f.Name.StartsWith( "__" ) && !f.IsImplicitlyDeclared ) )
23            {
24                builder.Advice.IntroduceAttribute( field, attribute );
25            }
26        }
27    }
28}
Source Code
1namespace Doc.AddEditorBrowsableAttribute
2{


3    [HideFieldsFromEditor]
4    public class C
5    {
6        public int NormalField;
7#pragma warning disable IDE1006 // Naming Styles
8        public string? __HiddenField;
9#pragma warning restore IDE1006 // Naming Styles

10    }
11}
Transformed Code
1using System.ComponentModel;
2
3namespace Doc.AddEditorBrowsableAttribute
4{
5    [HideFieldsFromEditor]
6    public class C
7    {
8        public int NormalField;
9#pragma warning disable IDE1006 // Naming Styles
10        [EditorBrowsable(EditorBrowsableState.Never)]
11        public string? __HiddenField;
12#pragma warning restore IDE1006 // Naming Styles
13    }
14}

Removing attributes from an existing declaration

To remove all custom attributes of a given type from a declaration, use the RemoveAttributes method.

Note that you cannot edit a custom attribute; instead, you must remove previous instances and add new ones.

Adding attributes to an introduced declaration, declaratively

When your aspect introduces a new declaration in a declarative way, i.e., using the [Introduce] custom attribute, you can add any custom attribute to the new member by adding the attribute to the template.

Example: declaratively introducing a field with EditorBrowsableAttribute

The next example demonstrates an aspect that introduces a field hidden from the editor by <xref:System.ComponentModel.EditorBrowsableAttribute>. The custom attribute is copied from the template to the target declaration.

1using Metalama.Framework.Aspects;
2using System.ComponentModel;
3
4namespace Doc.AddEditorBrowsableAttribute_Introduced_Declarative
5{
6    public class AddEditorHiddenFieldAttribute : TypeAspect
7    {
8        [Introduce]
9        [EditorBrowsable( EditorBrowsableState.Never )]
10#pragma warning disable IDE1006 // Naming Styles
11        public string? __HiddenField;
12#pragma warning restore IDE1006 // Naming Styles
13    }
14}
Source Code
1namespace Doc.AddEditorBrowsableAttribute_Introduced_Declarative
2{


3    [AddEditorHiddenFieldAttribute]
4    public class C
5    {
6        public int NormalField;
7    }
8}
Transformed Code
1using System.ComponentModel;
2
3namespace Doc.AddEditorBrowsableAttribute_Introduced_Declarative
4{
5    [AddEditorHiddenFieldAttribute]
6    public class C
7    {
8        public int NormalField;
9        [EditorBrowsable(EditorBrowsableState.Never)]
10        public string? __HiddenField;
11    }
12}

Adding attributes to an introduced declaration, programmatically

The second, more advanced method to introduce declarations into a type is to call one of the Introduce* methods of the IAdviceFactory interface. This technique is described in Introducing members. These methods accept an optional delegate that can configure the declaration being introduced. This delegate receives an IDeclarationBuilder, and you can use the AddAttribute and RemoveAttributes methods as described above.

Example: programmatically introducing a field with EditorBrowsableAttribute

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using Metalama.Framework.Code.DeclarationBuilders;
4using System.ComponentModel;
5
6namespace Doc.AddEditorBrowsableAttribute_Introduced_Programmatic
7{
8    public class AddEditorHiddenFieldAttribute : TypeAspect
9    {
10        public override void BuildAspect( IAspectBuilder<INamedType> builder )
11        {
12            var attribute = AttributeConstruction.Create(
13                typeof(EditorBrowsableAttribute),
14                new object[] { EditorBrowsableState.Never } );
15
16            builder.Advice.IntroduceField(
17                builder.Target,
18                "__HiddenField",
19                typeof(int),
20                buildField: f => f.AddAttribute( attribute ) );
21        }
22    }
23}
Source Code
1namespace Doc.AddEditorBrowsableAttribute_Introduced_Programmatic
2{


3    [AddEditorHiddenFieldAttribute]
4    public class C
5    {
6        public int NormalField;
7    }
8}
Transformed Code
1using System.ComponentModel;
2
3namespace Doc.AddEditorBrowsableAttribute_Introduced_Programmatic
4{
5    [AddEditorHiddenFieldAttribute]
6    public class C
7    {
8        public int NormalField;
9        [EditorBrowsable(EditorBrowsableState.Never)]
10        private int __HiddenField;
11    }
12}