Skip to content

Commit c0be549

Browse files
authored
Merge pull request #15 from postsharp/topic/doc-2026.0
Topic/doc 2026.0
2 parents 3434d3b + 4395367 commit c0be549

37 files changed

+908
-180
lines changed
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
---
2+
uid: extension-blocks-multicast
3+
title: "C# 14 extension blocks and multicasting"
4+
product: "postsharp"
5+
categories: "PostSharp;AOP;Metaprogramming"
6+
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."
7+
---
8+
# C# 14 extension blocks and multicasting
9+
10+
11+
## Overview
12+
13+
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.
14+
15+
## How extension blocks are implemented in IL
16+
17+
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.
18+
19+
At the IL level and when viewed through `System.Reflection`, the compiler generates:
20+
21+
- Static implementation methods (e.g., `ExtensionMethod` and `get_ExtensionProperty`)
22+
- Nested compiler-generated metadata types that describe the extension block structure
23+
24+
For example, consider this C# 14 extension block:
25+
26+
```csharp
27+
public static class TestClassExtensions
28+
{
29+
extension<TInstance>(TInstance instance)
30+
{
31+
public TInstance ExtensionMethod() => instance;
32+
33+
public TInstance ExtensionProperty => instance;
34+
}
35+
}
36+
```
37+
38+
The compiler generates nested types similar to:
39+
40+
```csharp
41+
public static class TestClassExtensions
42+
{
43+
// Compiler-generated nested metadata types representing the extension block.
44+
[SpecialName]
45+
private static class <G>$BA41CFE2B5EDAEB8C1B9062F59ED4D69<TInstance>
46+
{
47+
// Pure metadata classes.
48+
[SpecialName]
49+
public static class <M>$DE9F57644BDC66EDA3F1FD365749DA9F;
50+
51+
[SpecialName]
52+
public static class <M>$7A2C8F3E91B4D5A6C0E9F1B2D4A7C8E3;
53+
54+
55+
// Original extension members without implementations.
56+
[ExtensionMarker("<M>$DE9F57644BDC66EDA3F1FD365749DA9F")]
57+
public TInstance ExtensionMethod() => throw null;
58+
59+
[ExtensionMarker("<M>$DE9F57644BDC66EDA3F1FD365749DA9F")]
60+
public TInstance ExtensionProperty => throw null;
61+
}
62+
63+
// Static implementation methods with received parameter.
64+
public static TInstance ExtensionMethod<TInstance>(TInstance instance) => instance;
65+
66+
public static TInstance get_ExtensionProperty<TInstance>(TInstance instance) => instance;
67+
}
68+
```
69+
70+
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:
71+
72+
- **Extension block metadata types are always skipped** - These compiler-generated nested types and their members are never eligible for aspect application
73+
- **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`
74+
75+
## Enabling extension block support
76+
77+
To apply aspects to extension block members, one of the following actions needs to be taken:
78+
79+
- Add the aspect directly to the extension block member, or
80+
- Set the <xref:PostSharp.Extensibility.MulticastAttribute.AllowExtensionBlockMembers> property to `true` when multicasting the aspect from an outer scope, such as the declaring type.
81+
- Add <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute> to the aspect type and set <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.AllowExtensionBlockMembers> to `true`.
82+
83+
### Example: Applying aspects directly to extension members
84+
85+
When applied explicitly to extension members, aspects are added without the need to change any of the properties.
86+
87+
```csharp
88+
[PSerializable]
89+
class LoggingAspect : OnMethodBoundaryAspect
90+
{
91+
public override void OnEntry(MethodExecutionArgs args)
92+
{
93+
Console.WriteLine($"Entering: {args.Method.Name}");
94+
}
95+
}
96+
97+
public static class TestClassExtensions
98+
{
99+
extension<TInstance>(TInstance instance)
100+
{
101+
[LoggingAspect]
102+
public TInstance ExtensionMethod() => instance;
103+
104+
[LoggingAspect]
105+
public TInstance ExtensionProperty
106+
{
107+
get => instance;
108+
set { }
109+
}
110+
}
111+
}
112+
```
113+
114+
### Example: Multicasting with AllowExtensionBlockMembers set to true
115+
116+
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.
117+
118+
```csharp
119+
[PSerializable]
120+
class LoggingAspect : OnMethodBoundaryAspect
121+
{
122+
public override void OnEntry(MethodExecutionArgs args)
123+
{
124+
Console.WriteLine($"Entering: {args.Method.Name}");
125+
}
126+
}
127+
128+
[LoggingAspect(AllowExtensionBlockMembers = true)]
129+
public static class TestClassExtensions
130+
{
131+
extension<TInstance>(TInstance instance)
132+
{
133+
public TInstance ExtensionMethod() => instance;
134+
135+
public TInstance ExtensionProperty
136+
{
137+
get => instance;
138+
set { }
139+
}
140+
}
141+
}
142+
```
143+
144+
### Example: Changing default behavior for custom aspects
145+
146+
Setting the <xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.AllowExtensionBlockMembers> property to `true` on the aspect class will make extension members automatically eligible for the aspect:
147+
148+
```csharp
149+
[MulticastAttributeUsage(MulticastTargets.Method, AllowExtensionBlockMembers = true)]
150+
[PSerializable]
151+
public class LoggingAspect : OnMethodBoundaryAspect
152+
{
153+
public override void OnEntry(MethodExecutionArgs args)
154+
{
155+
Console.WriteLine($"Entering: {args.Method.Name}");
156+
}
157+
}
158+
159+
[LoggingAspect]
160+
public static class TestClassExtensions
161+
{
162+
extension<TInstance>(TInstance instance)
163+
{
164+
public TInstance ExtensionMethod() => instance;
165+
166+
public TInstance ExtensionProperty
167+
{
168+
get => instance;
169+
set { }
170+
}
171+
}
172+
}
173+
```
174+
175+
## IAspectProvider and IAdviceProvider
176+
177+
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.
178+
179+
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:
180+
181+
- Static implementation methods for extension members
182+
- Compiler-generated nested metadata types (e.g., `<Extension>0<TInstance>`)
183+
184+
Attempting to target extension block metadata types or their members will result in error **LA0167**.
185+
186+
To make your `IAspectProvider` or `IAdviceProvider` safe to use with extension blocks, remember that:
187+
188+
- **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.
189+
- **Extension block metadata types must be avoided** - Targeting these compiler-generated types or their members will result in error LA0167.
190+
191+
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.
192+
193+
### Example: Filtering extension block metadata
194+
195+
When implementing `IAspectProvider`, use `IsExtensionBlockMetadata` to skip compiler-generated metadata types:
196+
197+
```csharp
198+
using PostSharp.Aspects;
199+
using PostSharp.Reflection;
200+
using System;
201+
using System.Collections.Generic;
202+
using System.Linq;
203+
using System.Reflection;
204+
205+
[PSerializable]
206+
public class MyAspectProvider : IAspectProvider
207+
{
208+
public IEnumerable<AspectInstance> ProvideAspects(object targetElement)
209+
{
210+
Type type = (Type)targetElement;
211+
212+
// Get all nested types, but filter out extension block metadata
213+
var nestedTypes = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic)
214+
.Where(t => !ReflectionHelper.IsExtensionBlockMetadata(t));
215+
216+
foreach (var nestedType in nestedTypes)
217+
{
218+
// Safely process non-metadata nested types
219+
yield return new AspectInstance(nestedType, new MyAspect());
220+
}
221+
222+
// Extension member implementation methods are safe to target
223+
var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Static)
224+
.Where(m => !ReflectionHelper.IsExtensionBlockMetadata(m.DeclaringType));
225+
226+
foreach (var method in methods)
227+
{
228+
yield return new AspectInstance(method, new MyMethodAspect());
229+
}
230+
}
231+
}
232+
```
233+
234+
235+
236+
## Backward compatibility
237+
238+
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.
239+
240+
241+
## See Also
242+
243+
<xref:multicast-conceptual>
244+
<br><xref:attribute-multicasting>
245+
<br><xref:whats-new-20260>
246+
<br><xref:PostSharp.Extensibility.MulticastAttribute.AllowExtensionBlockMembers>
247+
<br><xref:PostSharp.Extensibility.MulticastAttributeUsageAttribute.AllowExtensionBlockMembers>
248+
<br><xref:PostSharp.Reflection.ReflectionHelper.IsExtensionBlockMetadata>
249+
<br><xref:iaspectprovider>
250+
251+
**Other Resources**
252+
<br>[C# 14 Extension Members - Microsoft Learn](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/extension)
253+
<br>[Exploring extension members - .NET Blog](https://devblogs.microsoft.com/dotnet/csharp-exploring-extension-members/)

conceptual/CustomPatterns/Aspects/Applying/iaspectprovider.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ You may have situations where you are looking to implement an aspect as part of
1111

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

14-
15-
###
16-
1714
1. Create an aspect that implements that <xref:PostSharp.Aspects.IAspectProvider> interface.
1815

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

7067
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.
7168

69+
## Extension Block Members
70+
71+
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).
72+
73+
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.
74+
7275
## See Also
7376

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

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

conceptual/DeploymentConfiguration/Deployment/requirements-20250.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ The following software components need to be installed before PostSharp can be u
5050
## Requirements on build servers
5151

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

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

0 commit comments

Comments
 (0)