Metalama//Conceptual documentation/Creating aspects/Applying aspects to derived types
Open sandboxFocus

Applying aspects to derived types

Many aspects, such as INotifyPropertyChanged implementation or thread synchronization aspects, need to be inherited from the base class to which the aspect is applied, to all derived classes. That is, if a base class has a [NotifyPropertyChanged] aspect that adds calls to OnPropertyChanged to all property setters, it is logical that the aspect also affects the property setters of the derived classes.

This feature is called aspect inheritance. It is activated by adding the [Inheritable] custom attribute to the aspect class. When an aspect is marked as inheritable, its BuildAspect method is no longer called only for the direct target declaration of the aspect, but also for all derived declarations.

An aspect can be inherited along the following lines:

  • from a base class to derived classes;
  • from a base interface to derived interfaces;
  • from an interface to all types implementing that interface;
  • from a virtual or abstract member to its override members;
  • from an interface member to its implementations;
  • from a parameter of a virtual or abstract method to the corresponding parameter of all override methods;
  • from a parameter of an interface member to the corresponding parameter of all its implementations.


The following type-level aspect is applied to a base class and is implicitly inherited by all derived classes.

1using Metalama.Framework.Aspects;
2using Metalama.Framework.Code;
3using System;
5namespace Doc.InheritedTypeLevel
7    [Inheritable]
8    internal class InheritedAspectAttribute : TypeAspect
9    {
10        public override void BuildAspect( IAspectBuilder<INamedType> builder )
11        {
12            foreach ( var method in builder.Target.Methods )
13            {
14                builder.Advice.Override( method, nameof(this.MethodTemplate) );
15            }
16        }
18        [Template]
19        private dynamic? MethodTemplate()
20        {
21            Console.WriteLine( "Hacked!" );
23            return meta.Proceed();
24        }
25    }
Source Code
1namespace Doc.InheritedTypeLevel

3    [InheritedAspect]
4    internal class BaseClass
5    {
6        public void Method1() { }

8        public virtual void Method2() { }
9    }


11    internal class DerivedClass : BaseClass
12    {
13        public override void Method2()
14        {
15            base.Method2();

16        }

18        public void Method3() { }
19    }


21    internal class DerivedTwiceClass : DerivedClass
22    {
23        public override void Method2()
24        {
25            base.Method2();

26        }

28        public void Method4() { }
29    }

Transformed Code
1using System;
3namespace Doc.InheritedTypeLevel
5    [InheritedAspect]
6    internal class BaseClass
7    {
8        public void Method1()
9        {
10            Console.WriteLine("Hacked!");
11            return;
12        }
14        public virtual void Method2()
15        {
16            Console.WriteLine("Hacked!");
17            return;
18        }
19    }
21    internal class DerivedClass : BaseClass
22    {
23        public override void Method2()
24        {
25            Console.WriteLine("Hacked!");
26            base.Method2();
27            return;
28        }
30        public void Method3()
31        {
32            Console.WriteLine("Hacked!");
33            return;
34        }
35    }
37    internal class DerivedTwiceClass : DerivedClass
38    {
39        public override void Method2()
40        {
41            Console.WriteLine("Hacked!");
42            base.Method2();
43            return;
44        }
46        public void Method4()
47        {
48            Console.WriteLine("Hacked!");
49            return;
50        }
51    }

Conditional inheritance

The [Inheritable] custom attribute causes all instances of the aspect class to be inheritable, regardless of their fields or properties. If you want to make the inheritance decision dependent on fields or properties of the aspect, then your aspect must implement the IConditionallyInheritableAspect.

Note that when the IConditionallyInheritableAspect interface is implemented, the refactoring menu will always suggest adding the aspect to a declaration, even if the aspect is eligible for inheritance only on the target declaration.

Cross-project inheritance

Aspect inheritance also works across project boundaries, even when the base class is in a different project than the derived class.

To make this possible, the aspect instance in the project containing the base class is serialized into a binary buffer and stored as a managed resource in the assembly. When compiling the project containing the derived class, the aspect is deserialized from the binary buffer, and its BuildAspect method can be called.

Serialization uses a custom formatter whose semantics are close to the legacy BinaryFormatter of the now obsolete [Serializable]. To mark a field or property as non-serializable, use the NonCompileTimeSerializedAttribute custom attribute.

Eligibility of inherited aspects

The eligibility of an aspect is a set of rules defining which target declarations an aspect can be legitimately applied to. For details, see Defining the eligibility of aspects.

When an aspect is inherited, it has two sets of eligibility rules:

  • the normal eligibility rules define on which declarations the aspect can be expanded; typically, this would not include any abstract members;
  • the inheritance eligibility rules define which declarations the aspect can be added to for inheritance; typically, this would include abstract members.

When an inherited aspect is added to a target that matches the inheritance eligibility rules but not the normal eligibility rules, an abstract aspect instance is added to that target. That is, BuildAspect method is not called for that target, but only for derived targets.

To define the eligibility rules that do not apply to the inheritance scenario, use the BuildEligibility method, use the ExceptForInheritance method.


The following implementation of BuildEligibility specifies that the aspect will be applied abstractly when applied to an abstract method, that its BuildAspect method will not be invoked for the abstract method but only for methods implementing the abstract method.

public override void BuildEligibility( IEligibilityBuilder<IMethod> builder )