Open sandboxFocusImprove this doc

C# 14 Extension Blocks and Multicasting

Overview

Starting with PostSharp 2026.0, the multicast engine provides support for C# 14 extension blocks. By default, aspects and multicast attributes are not applied to extension block members to prevent unexpected behavior. You can explicitly enable this using the AllowExtensionBlockMembers property.

How extension blocks are implemented in IL

The C# compiler implements extension block members as static methods (including properties) and a set of special metadata types. These metadata types are intended for the C# compiler to match extension methods and properties with the receiver type.

At the IL level and when viewed through System.Reflection, the compiler generates:

  • Static implementation methods (e.g., ExtensionMethod and get_ExtensionProperty)
  • Nested compiler-generated metadata types that describe the extension block structure

For example, consider this C# 14 extension block:

public static class TestClassExtensions
{
    extension<TInstance>( TInstance instance )
    {
        public TInstance ExtensionMethod() => instance;

        public TInstance ExtensionProperty => instance;
    }
}

The compiler generates nested types similar to:

public static class TestClassExtensions
{
    // Compiler-generated nested metadata types representing the extension block.
    [SpecialName]
    private static class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69<TInstance>
    {
        // Pure metadata classes.
        [SpecialName]
    	public static class <M>$DE9F57644BDC66EDA3F1FD365749DA9F;

        [SpecialName]
    	public static class <M>$7A2C8F3E91B4D5A6C0E9F1B2D4A7C8E3;
    	

        // Original extension members without implementations.
        [ExtensionMarker("<M>$DE9F57644BDC66EDA3F1FD365749DA9F")]
        public TInstance ExtensionMethod() => throw null;

        [ExtensionMarker("<M>$DE9F57644BDC66EDA3F1FD365749DA9F")]
        public TInstance ExtensionProperty => throw null;
    }

    // Static implementation methods with received parameter.
    public static TInstance ExtensionMethod<TInstance>(TInstance instance) => instance;

    public static TInstance get_ExtensionProperty<TInstance>(TInstance instance) => instance;
}

Since both the implementation methods and the metadata types may not be expected by existing aspects, the multicasting algorithm in PostSharp 2026.0 and later behaves as follows:

  • Extension block metadata types are always skipped - These compiler-generated nested types and their members are never eligible for aspect application
  • Extension block implementation methods require opt-in - Static methods that implement extension members are skipped by default but can be targeted by setting AllowExtensionBlockMembers = true

Enabling extension block support

To apply aspects to extension block members, do one of the following:

Example: Applying aspects directly to extension members

When applied explicitly to extension methods or to the accessors of extension properties, aspects are added without the need to change any of the properties.

[PSerializable]
class LoggingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering: {args.Method.Name}" );
    }
}

public static class TestClassExtensions
{
    extension<TInstance>( TInstance instance )
    {
        [LoggingAspect]
        public TInstance ExtensionMethod() => instance;

        public TInstance ExtensionProperty
        {
            [LoggingAspect]
            get => instance;
            [LoggingAspect]
            set { }
        }
    }
}

You can also add a method-level aspect to the extension property itself. It is then applied to both accessors, as for an ordinary property.

Note

A location-level aspect, such as LocationInterceptionAspect, cannot be applied to an extension property, because an extension property has no implementation of its own. PostSharp reports the error LA0252. Use a method-level aspect instead.

A program that calls the extension method, then gets and sets the extension property, prints:

Entering: ExtensionMethod
Hello
Entering: get_ExtensionProperty
Hello
Entering: set_ExtensionProperty

Example: Multicasting with AllowExtensionBlockMembers set to true

After setting AllowExtensionBlockMembers to true and multicasting on the whole class, members within extension blocks will be eligible for the aspect.

[PSerializable]
class LoggingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering: {args.Method.Name}" );
    }
}

[LoggingAspect( AllowExtensionBlockMembers = true )]
public static class TestClassExtensions
{
    extension<TInstance>( TInstance instance )
    {
        public TInstance ExtensionMethod() => instance;

        public TInstance ExtensionProperty
        {
            get => instance;
            set { }
        }
    }
}

The aspect is applied to the extension method and to both accessors of the extension property:

Entering: ExtensionMethod
Hello
Entering: get_ExtensionProperty
Hello
Entering: set_ExtensionProperty

Example: Changing default behavior for custom aspects

Setting the AllowExtensionBlockMembers property to true on the aspect class will make extension members automatically eligible for the aspect:

[MulticastAttributeUsage( MulticastTargets.Method, AllowExtensionBlockMembers = true )]
[PSerializable]
public class LoggingAspect : OnMethodBoundaryAspect
{
    public override void OnEntry( MethodExecutionArgs args )
    {
        Console.WriteLine( $"Entering: {args.Method.Name}" );
    }
}

[LoggingAspect]
public static class TestClassExtensions
{
    extension<TInstance>( TInstance instance )
    {
        public TInstance ExtensionMethod() => instance;

        public TInstance ExtensionProperty
        {
            get => instance;
            set { }
        }
    }
}

IAspectProvider and IAdviceProvider

C# 14 extension blocks present a challenge when using IAspectProvider or IAdviceProvider because the System.Reflection API exposes the compiler-generated IL artifacts rather than the source-level C# syntax.

When implementing IAspectProvider or IAdviceProvider, you receive reflection objects (Type, MethodInfo, etc.) that represent the IL structure. For extension blocks, this means you will encounter:

  • Static implementation methods for extension members
  • Compiler-generated nested metadata types (e.g., <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69<TInstance>)

Attempting to target extension block metadata types or their members will result in error LA0167. For instance, the following aspect provider adds an aspect to the methods of all nested types, including the extension block metadata types:

[PSerializable]
public class MyAspectProvider : TypeLevelAspect, IAspectProvider
{
    public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
    {
        Type type = (Type) targetElement;

        // Error: the nested types include the extension block metadata types.
        foreach ( var nestedType in type.GetNestedTypes( BindingFlags.Public | BindingFlags.NonPublic ) )
        {
            foreach ( var method in nestedType.GetMethods( BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly ) )
            {
                yield return new AspectInstance( method, new MyMethodAspect() );
            }
        }
    }
}

The build then fails with the following errors:

error LA0167: ExtensionBlockMetadataError.cs(44,26): Cannot apply the aspect "ExtensionBlockMetadataError.MyMethodAspect" to "ExtensionBlockMetadataError.TestClassExtensions.<G>$8048A6C8BE30A622530249B904B537EB`1<$T0>.ExtensionMethod": applying an aspect to compiler-generated method is not supported. Use MemberInfo.GetSemanticInfo().IsSelectable to evaluate whether the declaration is a legitimate aspect target.
error LA0167: ExtensionBlockMetadataError.cs(46,47): Cannot apply the aspect "ExtensionBlockMetadataError.MyMethodAspect" to "ExtensionBlockMetadataError.TestClassExtensions.<G>$8048A6C8BE30A622530249B904B537EB`1<$T0>.get_ExtensionProperty": applying an aspect to compiler-generated method is not supported. Use MemberInfo.GetSemanticInfo().IsSelectable to evaluate whether the declaration is a legitimate aspect target.

To make your IAspectProvider or IAdviceProvider safe to use with extension blocks, remember that:

  • Extension member implementation methods are safe to target - These are the static methods that implement the actual extension logic and can have aspects applied to them.
  • Extension block metadata types must be avoided - Targeting these compiler-generated types or their members will result in error LA0167.

PostSharp 2026.0 introduces the <xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata> method to help identify and filter extension block metadata artifacts. This method allows you to distinguish between regular types/members and compiler-generated extension block metadata.

Example: Filtering extension block metadata

When implementing IAspectProvider, use IsExtensionBlockMetadata to skip compiler-generated metadata types:

[PSerializable]
public class MyAspectProvider : TypeLevelAspect, IAspectProvider
{
    public IEnumerable<AspectInstance> ProvideAspects( object targetElement )
    {
        Type type = (Type) targetElement;

        // Get all nested types, but filter out extension block metadata
        var nestedTypes = type.GetNestedTypes( BindingFlags.Public | BindingFlags.NonPublic )
            .Where( t => !ReflectionHelper.IsExtensionBlockMetadata( t ) );

        foreach ( var nestedType in nestedTypes )
        {
            // Safely process non-metadata nested types
            yield return new AspectInstance( nestedType, new MyAspect() );
        }

        // Extension member implementation methods are safe to target
        var methods = type.GetMethods( BindingFlags.Public | BindingFlags.Static )
            .Where( m => !ReflectionHelper.IsExtensionBlockMetadata( m ) );

        foreach ( var method in methods )
        {
            yield return new AspectInstance( method, new MyMethodAspect() );
        }
    }
}

Backward compatibility

Classic extension methods using the this modifier continue to work as before and are not affected by the AllowExtensionBlockMembers property. They are always eligible for aspect application according to existing multicast rules.

See Also

Reference

AllowExtensionBlockMembers
AllowExtensionBlockMembers
<xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata>

Other Resources

Understanding Attribute Multicasting
Adding Aspects to Multiple Declarations Using Attributes
What's New in PostSharp 2026.0
Adding Aspects Programmatically Using IAspectProvider
C# 14 Extension Members - Microsoft Learn
Exploring extension members - .NET Blog