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.,
ExtensionMethodandget_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, one of the following actions needs to be taken:
- Add the aspect directly to the extension block member, or
- Set the AllowExtensionBlockMembers property to
truewhen multicasting the aspect from an outer scope, such as the declaring type. - Add MulticastAttributeUsageAttribute to the aspect type and set AllowExtensionBlockMembers to
true.
Example: Applying aspects directly to extension members
When applied explicitly to extension members, 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;
[LoggingAspect]
public TInstance ExtensionProperty
{
get => instance;
set { }
}
}
}
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 { }
}
}
}
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'll encounter:
- Static implementation methods for extension members
- Compiler-generated nested metadata types (e.g.,
<Extension>0<TInstance>)
Attempting to target extension block metadata types or their members will result in error LA0167.
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:
using PostSharp.Aspects;
using PostSharp.Reflection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
[PSerializable]
public class MyAspectProvider : 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.DeclaringType));
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
Understanding Attribute Multicasting
Adding Aspects to Multiple Declarations Using Attributes
What's New in PostSharp 2026.0
AllowExtensionBlockMembers
AllowExtensionBlockMembers
<xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata>
Adding Aspects Programmatically using IAspectProvider
Other Resources
C# 14 Extension Members - Microsoft Learn
Exploring extension members - .NET Blog