Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3b3d91b
# 36974 Update the doc for 2026.0
addabis Nov 19, 2025
9af2199
Merge branch 'master' of https://github.com/postsharp/PostSharp.Docum…
addabis Nov 20, 2025
133e5d4
Remove deadlock detection doc, deprecate undo-redo.
addabis Nov 30, 2025
0373d66
Cleaning up.
addabis Dec 1, 2025
f39b61c
Cleanup references to undo-redo in the rest of the doc.
addabis Dec 1, 2025
48edcaa
Document extension block multicast support.
addabis Dec 1, 2025
f8693cb
Finished review changes.
addabis Dec 1, 2025
aec5052
Fixed formatting.
addabis Dec 1, 2025
bb971c9
Edit the new file to restore state overwritten by Claude Code.
addabis Dec 2, 2025
0d927ab
Fixed grammar.
addabis Dec 2, 2025
e47b01f
Update the note about aspect/advice provider.
addabis Dec 2, 2025
7f4c893
Fixes.
addabis Dec 5, 2025
7b61c13
Minor formatting fix.
addabis Dec 5, 2025
fa19272
Moved IAspectProvider note about extension member to the appropriate …
addabis Dec 5, 2025
8dccf39
Apply suggestions from code review
addabis Dec 5, 2025
6729e99
Clarification.
addabis Dec 5, 2025
2b11642
Merge branch 'topic/doc-2026.0' of https://github.com/postsharp/PostS…
addabis Dec 5, 2025
93456d1
Added reference to changes in IAspectProvider to What's new.
addabis Dec 5, 2025
7a1d532
Simplified, added xref.
addabis Dec 5, 2025
07361cf
Clarified.
addabis Dec 5, 2025
96102c5
Review.
gfraiteur Dec 16, 2025
be5cdc0
Fixed the extension block generated code.
gfraiteur Dec 16, 2025
4395367
Clarified deprecation of target frameworks in breaking changes docume…
gfraiteur Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
---
uid: extension-blocks-multicast
title: "C# 14 extension blocks and multicasting"
product: "postsharp"
categories: "PostSharp;AOP;Metaprogramming"
summary: "Explains how C# 14 extension blocks are handled by PostSharp's multicast engine in PostSharp 2026.0, including the AllowExtensionBlockMembers property and ReflectionHelper.IsExtensionBlockMetadata method."
---
# 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 <xref:PostSharp.Extensibility.MulticastAttribute.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:

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

public TInstance ExtensionProperty => instance;
}
}
```

The compiler generates nested types similar to:

```csharp
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 <xref:PostSharp.Extensibility.MulticastAttribute.AllowExtensionBlockMembers> property to `true` when multicasting the aspect from an outer scope, such as the declaring type.
- Add <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute> to the aspect type and set <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.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.

```csharp
[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 <xref:PostSharp.Extensibility.MulticastAttribute.AllowExtensionBlockMembers> to `true` and multicasting on the whole class, members within extension blocks will be eligible for the aspect.

```csharp
[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 <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.AllowExtensionBlockMembers> property to `true` on the aspect class will make extension members automatically eligible for the aspect:

```csharp
[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 <xref:PostSharp.Aspects.IAspectProvider> or <xref:PostSharp.Aspects.Advices.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:

```csharp
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

<xref:multicast-conceptual>
<br><xref:attribute-multicasting>
<br><xref:whats-new-20260>
<br><xref:PostSharp.Extensibility.MulticastAttribute.AllowExtensionBlockMembers>
<br><xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.AllowExtensionBlockMembers>
<br><xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata>
<br><xref:iaspectprovider>

**Other Resources**
<br>[C# 14 Extension Members - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension)
<br>[Exploring extension members - .NET Blog](https://devblogs.microsoft.com/dotnet/csharp-exploring-extension-members/)
14 changes: 10 additions & 4 deletions conceptual/CustomPatterns/Aspects/Applying/iaspectprovider.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ You may have situations where you are looking to implement an aspect as part of

The theoretical concept can cause some mental gymnastics, so let's take a look at the implementation.


###

1. Create an aspect that implements that <xref:PostSharp.Aspects.IAspectProvider> interface.

```csharp
Expand Down Expand Up @@ -69,6 +66,12 @@ It is common that aspects provided by <xref:PostSharp.Aspects.IAspectProvider> (

An interesting feature of PostSharp is that object graphs instantiated at compile-time are serialized, and can be used at run-time. In other words, if you store a reference to another aspect in a child aspect, you will be able to use this reference at run time.

## Extension Block Members

C# 14 introduced extension members. Both <xref:PostSharp.Aspects.IAspectProvider> and <xref:PostSharp.Aspects.Advices.IAdviceProvider> can be used on extension member implementation methods (members of the class containing extension blocks).

Targeting extension block metadata types (nested types emitted by the C# compiler) and all their members will result in an error `LA0167`. You can use <xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata> to identify extension block metadata members if filtering is needed.

## See Also

**Reference**
Expand All @@ -77,8 +80,11 @@ An interesting feature of PostSharp is that object graphs instantiated at compil
<br><xref:PostSharp.Patterns.Model.NotifyPropertyChangedAttribute>
<br><xref:PostSharp.Aspects.IAspectProvider.ProvideAspects(System.Object)>
<br><xref:PostSharp.Aspects.AspectInstance>
<br>**Other Resources**
<br><br>**Other Resources**

<xref:inotifypropertychanged>
<br><xref:inotifypropertychanged-customization>
<br>[C# 14 Extension Members - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension)
<br>[Exploring extension members - .NET Blog](https://devblogs.microsoft.com/dotnet/csharp-exploring-extension-members/)

<br>
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The following software components need to be installed before PostSharp can be u
## Requirements on build servers

* Any of the following operating systems currently in mainstream support by respective vendors:
* Windows Server 2022 x64 or ARM64
* Windows Server 2022
* Windows 10 x64
* Windows 11 x64 or ARM64
* Ubuntu 20.04 LTS x64,
Expand All @@ -61,7 +61,7 @@ The following software components need to be installed before PostSharp can be u
* Alpine Linux 3.18+ ARM64.

At least one of the following:
* .NET Framework 4.7.2 and later + Build Tools for Visual Studio 2022 or Visual Studio 2022 (17.8 and later).
* .NET Framework 4.7.2 and later + Build Tools for Visual Studio 2022 or Visual Studio 2022 (17.10 and later).
* .NET SDK 8.0 (build 8.0.100 or later).
* .NET SDK 9.0 (build 9.0.100 or later).

Expand Down
Loading